1

I tried the spring batch retry in this example. Retry feature is not working in Spring Batch and it works. I am trying to achieve the same with retrytemplate, but couldn't see the retry not working when thrown exception.

        @Configuration
            @EnableBatchProcessing
        //@EnableRetry
        public class RetryBatchJob {

          @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(2)
              .reader(itemReader())
              .writer(itemWriter())
              /*.faultTolerant()
              .retryLimit(5)
              .retry(Exception.class)*/
              .build();
          }

          @Bean  
          public Job job() {
            Job job = null;
            try {
              job = retryTemplate().execute(new RetryCallback<Job, Throwable>() {
                @Override
                public Job doWithRetry(RetryContext context) throws Throwable {
                  return jobs.get("job")
                    .start(step())
                    .build();
                }
              });
            } catch (Throwable throwable) {
              throwable.printStackTrace();
            }
            return job;
          }

          public static void main(String[] args) throws Exception {
            ApplicationContext context = new AnnotationConfigApplicationContext(RetryBatchJob.class);
            JobLauncher jobLauncher = context.getBean(JobLauncher.class);
            Job job = context.getBean(Job.class);
            jobLauncher.run(job, new JobParameters());
          }

          @Bean
          public RetryTemplate retryTemplate() {
            RetryTemplate retryTemplate = new RetryTemplate();

            SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(5, singletonMap(Exception.class, true));
            retryPolicy.setMaxAttempts(5);
            retryTemplate.setRetryPolicy(retryPolicy);

            return retryTemplate;
          }

        }

Am I missing something when I use RetryTemplate? I tried declarative configuration on step and job methods too, but no luck.

@Retryable(value = {Exception.class},
 maxAttemptsExpression = "5"
)

Note: using spring-retry 1.2.2 RELEASE.

user923499
  • 305
  • 1
  • 6
  • 18

2 Answers2

1

Thrown exceptions during job execution do not stop the execution, the job continue until it returns the execution result either FAILED OR COMPLETED ... Which makes it a positive result for RetryTemplate.execute(). You can take advantage of the execution status returned to throw a runtimeException in case of failure.

RetryTemplate template = new RetryTemplate();

    ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
    exponentialBackOffPolicy.setInitialInterval(5000);
    exponentialBackOffPolicy.setMultiplier(ExponentialBackOffPolicy.DEFAULT_MULTIPLIER);
    exponentialBackOffPolicy.setMaxInterval(ExponentialBackOffPolicy.DEFAULT_MAX_INTERVAL);

    Map<Class<? extends Throwable>, Boolean> exceptions = new HashMap<>();
    exceptions.put(Exception.class, true);
    SimpleRetryPolicy policy = new SimpleRetryPolicy(3, exceptions);

    template.setRetryPolicy(policy);
    template.setBackOffPolicy(exponentialBackOffPolicy);

    template.execute(new RetryCallback<JobExecution, Exception>() {

        @Override
        public JobExecution doWithRetry(RetryContext context) throws Exception {        
            return runJob(job, paramMap);
        }
    });

Function: runJob()

public JobExecution runJob(Job job, Map<String, JobParameter> paramMap) throws Exception {
    JobExecution exe = jobLauncher.run(job, new JobParameters(paramMap));       
    if(exe.getStatus().equals(BatchStatus.FAILED))
        throw new RuntimeException(exe.toString());
    return exe;     
}
MOURAD404
  • 11
  • 2
0

The method @Bean public Job job() { ... } is intended to define a Spring Bean of type Job. According to your code, you are calling retryTemplate().execute inside this method and expecting the job to be retried. This is not correct.

What you can try is first define your job like:

@Bean  
public Job job() {
    return jobs.get("job")
                .start(step())
                .build();
}

then call the retry template on the job bean you get from the application context in the main method, something like:

public static void main(String[] args) throws Exception {
        ApplicationContext context = new AnnotationConfigApplicationContext(RetryBatchJob.class);
        JobLauncher jobLauncher = context.getBean(JobLauncher.class);
        Job job = context.getBean(Job.class);
        RetryTemplate retryTemplate = context.getBean(RetryTemplate.class);
        retryTemplate.execute(new RetryCallback<JobExecution, Exception>() {
            @Override
            public JobExecution doWithRetry(RetryContext context) throws Exception {
                return jobLauncher.run(job, new JobParameters());
            };
        });
}

If I understand correctly, you are trying to automatically retry a failed job with a retry template, something similar to what is suggested in this PR: https://github.com/spring-projects/spring-batch/pull/440.

Anyway, I hope the example helps.

Mahmoud Ben Hassine
  • 28,519
  • 3
  • 32
  • 50
  • 2
    I tried your suggestion too calling the retrytemplate with application context, but the retry didn't happen when exception was thrown. – user923499 Oct 15 '18 at 04:16
  • I had to add the check jobExecution.getStatus().equals(BatchStatus.FAILED) and throw a Custom Exception inside the doWithRetry-Method. After that change the retry worked as expected. MOURAD404 showed this solution in his answer. – denfri.dev Apr 30 '20 at 07:12