Search code examples
springspring-batchretrytemplate

Retry feature is not working in Spring Batch


I have a batch job where i am using ScriptBatch.3.0.x version.

My use-case is to retry the job incase of any intermediate failures in between.

I am using the Chunk based processing and StepBuilderFactory for a job. I could not see any difference by adding the retry in it.

    return stepBuilderFactory.get("ValidationStepName")
            .<Long, Info> chunk(10)
            .reader(.....)
            .processor(.....)
    //        .faultTolerant()
    //        .retryLimit(5)
    //        .retryLimit(5).retry(Exception.class)
            .writer(......)
            .faultTolerant()
            .retryLimit(5)
            //.retryLimit(5).retry(Exception.class)
            .transactionManager(jpaTransactionManager())
            .listener(new ChunkNotificationListener())
            .build();

Not sure i am missing something here, I am expecting here that adding retryLimit() will retry the same chunk for n number of time on getting any exception


Solution

  • I am expecting here that adding retryLimit() will retry the same chunk for n number of time on getting any exception

    If you specify a retry limit, you need to specify which exceptions to retry. Otherwise you would have an IllegalStateException with the message: If a retry limit is provided then retryable exceptions must also be specified.

    EDIT:

    Point 1 : The following test is passing with version 3.0.9:

    import java.util.Arrays;
    
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.ExpectedException;
    import org.junit.runner.RunWith;
    import org.mockito.Mock;
    import org.mockito.junit.MockitoJUnitRunner;
    
    import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
    import org.springframework.batch.core.repository.JobRepository;
    import org.springframework.batch.core.step.tasklet.TaskletStep;
    import org.springframework.batch.item.support.ListItemReader;
    import org.springframework.batch.item.support.ListItemWriter;
    import org.springframework.transaction.PlatformTransactionManager;
    
    @RunWith(MockitoJUnitRunner.class)
    public class TestRetryConfig {
    
        @Rule
        public ExpectedException expectedException = ExpectedException.none();
        @Mock
        private JobRepository jobRepository;
        @Mock
        PlatformTransactionManager transactionManager;
    
        @Test
        public void testRetryLimitWithoutException() {
            expectedException.expect(IllegalStateException.class);
            expectedException.expectMessage("If a retry limit is provided then retryable exceptions must also be specified");
    
            StepBuilderFactory stepBuilderFactory = new StepBuilderFactory(jobRepository, transactionManager);
    
            TaskletStep step = stepBuilderFactory.get("step")
                    .<Integer, Integer>chunk(2)
                    .reader(new ListItemReader<>(Arrays.asList(1, 2, 3)))
                    .writer(new ListItemWriter<>())
                    .faultTolerant()
                    .retryLimit(3)
                    .build();
        }
    }
    

    It shows that if you specify a retry limit without the exception type(s) to retry, the step configuration should fail.

    Point 2: The following sample shows that the declared exception type is retried as expected (tested with version 3.0.9 too):

    import java.util.Arrays;
    
    import org.springframework.batch.core.Job;
    import org.springframework.batch.core.JobParameters;
    import org.springframework.batch.core.Step;
    import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
    import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
    import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
    import org.springframework.batch.core.launch.JobLauncher;
    import org.springframework.batch.item.ItemReader;
    import org.springframework.batch.item.ItemWriter;
    import org.springframework.batch.item.support.ListItemReader;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @EnableBatchProcessing
    public class MyJob {
    
        @Autowired
        private JobBuilderFactory jobs;
    
        @Autowired
        private StepBuilderFactory steps;
    
        @Bean
        public ItemReader<Integer> itemReader() {
            return new ListItemReader<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
        }
    
        @Bean
        public ItemWriter<Integer> itemWriter() {
            return items -> {
                for (Integer item : items) {
                    System.out.println("item = " + item);
                    if (item.equals(7)) {
                        throw new Exception("Sevens are sometime nasty, let's retry them");
                    }
                }
            };
        }
    
        @Bean
        public Step step() {
            return steps.get("step")
                    .<Integer, Integer>chunk(5)
                    .reader(itemReader())
                    .writer(itemWriter())
                    .faultTolerant()
                    .retryLimit(3)
                    .retry(Exception.class)
                    .build();
        }
    
        @Bean
        public Job job() {
            return jobs.get("job")
                    .start(step())
                    .build();
        }
    
        public static void main(String[] args) throws Exception {
            ApplicationContext context = new AnnotationConfigApplicationContext(MyJob.class);
            JobLauncher jobLauncher = context.getBean(JobLauncher.class);
            Job job = context.getBean(Job.class);
            jobLauncher.run(job, new JobParameters());
        }
    
    }
    

    it prints:

    item = 1
    item = 2
    item = 3
    item = 4
    item = 5
    item = 6
    item = 7
    item = 6
    item = 7
    item = 6
    item = 7
    

    item 7 is retried 3 times and then the step fails as expected.

    I hope this helps.