admin管理员组

文章数量:1401673

I'm using Spring Batch with resourceless job repository and the @Retryable annotation is not handling exceptions.

I tested the annotation with a service and it worked just fine. I also tried enabling the data source dependency but same result. And moving the @EnableRetry to the scheduler or to the job config didn't work either.

2025-03-23T10:49:06.099-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.b.c.l.s.TaskExecutorJobLauncher      : Job: [SimpleJob: [name=taskletJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 1s12ms
2025-03-23T10:50:05.125-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.ScheduledJobLauncher   : Lock: RedisLock [lockKey=lock00:lock00,lockedAt=2025-03-23@10:49:05.079, clientId=7262683c-13cf-4217-aada-5971491a9b00]
2025-03-23T10:50:05.128-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.ScheduledJobLauncher   : Lock Acquired: true
2025-03-23T10:50:05.128-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.b.c.l.s.TaskExecutorJobLauncher      : Job: [SimpleJob: [name=taskletJob]] launched with the following parameters: [{}]
2025-03-23T10:50:05.129-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [sampleStep]
2025-03-23T10:50:05.129-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.SampleTasklet          : Executing Tasklet...
2025-03-23T10:50:05.129-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.SampleTasklet          : Random: 1
2025-03-23T10:50:05.129-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.SampleTasklet          : Odd number
2025-03-23T10:50:05.130-03:00 ERROR 54498 --- [job] [   scheduling-1] o.s.batch.core.step.AbstractStep         : Encountered an error executing step sampleStep in job taskletJob

java.lang.IllegalStateException: Failing job...
    at com.batch.job.job.SampleTasklet.execute(SampleTasklet.java:31) ~[classes/:na]
    at .springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:383) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:307) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-6.2.3.jar:6.2.3]
    at .springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:250) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:369) ~[spring-batch-infrastructure-5.2.1.jar:5.2.1]
    at .springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:206) ~[spring-batch-infrastructure-5.2.1.jar:5.2.1]
    at .springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:140) ~[spring-batch-infrastructure-5.2.1.jar:5.2.1]
    at .springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:235) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:230) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:153) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:408) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:127) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:307) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.launch.support.TaskExecutorJobLauncher$1.run(TaskExecutorJobLauncher.java:155) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-6.2.3.jar:6.2.3]
    at .springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:146) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at com.batch.job.job.ScheduledJobLauncher.launchJob(ScheduledJobLauncher.java:49) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
    at .springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) ~[spring-context-6.2.3.jar:6.2.3]
    at .springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) ~[spring-context-6.2.3.jar:6.2.3]
    at io.micrometer.observation.Observation.observe(Observation.java:498) ~[micrometer-observation-1.14.4.jar:1.14.4]
    at .springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) ~[spring-context-6.2.3.jar:6.2.3]
    at .springframework.scheduling.config.Task$OutcomeTrackingRunnable.run(Task.java:85) ~[spring-context-6.2.3.jar:6.2.3]
    at .springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-6.2.3.jar:6.2.3]
    at .springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:96) ~[spring-context-6.2.3.jar:6.2.3]
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

2025-03-23T10:50:05.133-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [sampleStep] executed in 3ms
2025-03-23T10:50:05.133-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.b.c.l.s.TaskExecutorJobLauncher      : Job: [SimpleJob: [name=taskletJob]] completed with the following parameters: [{}] and the following status: [FAILED] in 4ms

SampleTasklet.java

@Configuration
@EnableRetry
@Slf4j
public class SampleTasklet implements Tasklet, InitializingBean {

  @Retryable(maxAttempts=2, backoff=@Backoff(delay=100, maxDelay=300))
  public RepeatStatus execute(StepContribution contribution,
                              ChunkContext chunkContext) throws Exception {
    log.debug("Executing Tasklet...");

    int randomNum = RandomGenerator.getDefault().nextInt(0, 10);
    log.debug("Random: " + randomNum);

    if ((randomNum & 1) != 0) {
      log.debug("Odd number");
      throw new IllegalStateException("Failing job...");
    }

    int delay = 1000;
    log.debug("Delaying " + delay + "ms");
    Thread.sleep(delay);

    log.debug("Tasklet executed!");
    return RepeatStatus.FINISHED;
  }

  public void afterPropertiesSet() throws Exception {}
}

JobConfig.java

@Configuration
public class JobConfig {

  @Bean
  public Job taskletJob(JobRepository jobRepository, Step step) {
    return new JobBuilder("taskletJob", jobRepository)
      .start(step)
      .build();
  }

  @Bean
  public Step sampleStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
    return new StepBuilder("sampleStep", jobRepository)
      .tasklet(new SampleTasklet(), transactionManager)
      .build();
  }
}

ScheduledJobLauncher.java

@Configuration
@EnableScheduling
@Slf4j
public class ScheduledJobLauncher {
  @Autowired
  JobLauncher jobLauncher;

  @Autowired
  Job job;

  @Autowired
  private ExpirableLockRegistry lockRegistry;

  @Value("${job.lock.registryKey}")
  private String registryKey;

  @Value("${job.lock.maxWaitTime}")
  private Integer maxWaitTime;

  @Scheduled(cron = "${job.scheduling.cron}")
  public void launchJob() throws Exception {
    Lock lock = lockRegistry.obtain(registryKey);
    log.debug("Lock: " + lock.toString());

    boolean lockAcquired =  lock.tryLock(maxWaitTime, TimeUnit.SECONDS);
    log.debug("Lock Acquired: " + lockAcquired);

    if (!lockAcquired) {
      throw new Exception("Lock not acquired");
    }

    try {
      jobLauncher.run(job, new JobParameters());
    } finally {
      lock.unlock();
    }
  }
}

BatchConfig.java

@Configuration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class BatchConfig {
  @Bean
  public PlatformTransactionManager transactionManager() {
    return new ResourcelessTransactionManager();
  }

  @Bean
  public JobRepository jobRepository() {
    return new ResourcelessJobRepository();
  }

  @Bean
  public JobLauncher jobLauncher(JobRepository jobRepository) {
    TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
    jobLauncher.setJobRepository(jobRepository);
    return jobLauncher;
  }
}

I'm using Spring Batch with resourceless job repository and the @Retryable annotation is not handling exceptions.

I tested the annotation with a service and it worked just fine. I also tried enabling the data source dependency but same result. And moving the @EnableRetry to the scheduler or to the job config didn't work either.

2025-03-23T10:49:06.099-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.b.c.l.s.TaskExecutorJobLauncher      : Job: [SimpleJob: [name=taskletJob]] completed with the following parameters: [{}] and the following status: [COMPLETED] in 1s12ms
2025-03-23T10:50:05.125-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.ScheduledJobLauncher   : Lock: RedisLock [lockKey=lock00:lock00,lockedAt=2025-03-23@10:49:05.079, clientId=7262683c-13cf-4217-aada-5971491a9b00]
2025-03-23T10:50:05.128-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.ScheduledJobLauncher   : Lock Acquired: true
2025-03-23T10:50:05.128-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.b.c.l.s.TaskExecutorJobLauncher      : Job: [SimpleJob: [name=taskletJob]] launched with the following parameters: [{}]
2025-03-23T10:50:05.129-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.batch.core.job.SimpleStepHandler     : Executing step: [sampleStep]
2025-03-23T10:50:05.129-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.SampleTasklet          : Executing Tasklet...
2025-03-23T10:50:05.129-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.SampleTasklet          : Random: 1
2025-03-23T10:50:05.129-03:00 DEBUG 54498 --- [job] [   scheduling-1] com.batch.job.job.SampleTasklet          : Odd number
2025-03-23T10:50:05.130-03:00 ERROR 54498 --- [job] [   scheduling-1] o.s.batch.core.step.AbstractStep         : Encountered an error executing step sampleStep in job taskletJob

java.lang.IllegalStateException: Failing job...
    at com.batch.job.job.SampleTasklet.execute(SampleTasklet.java:31) ~[classes/:na]
    at .springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:383) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:307) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140) ~[spring-tx-6.2.3.jar:6.2.3]
    at .springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:250) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:82) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:369) ~[spring-batch-infrastructure-5.2.1.jar:5.2.1]
    at .springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:206) ~[spring-batch-infrastructure-5.2.1.jar:5.2.1]
    at .springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:140) ~[spring-batch-infrastructure-5.2.1.jar:5.2.1]
    at .springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:235) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:230) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:153) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:408) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:127) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:307) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.batch.core.launch.support.TaskExecutorJobLauncher$1.run(TaskExecutorJobLauncher.java:155) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at .springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-6.2.3.jar:6.2.3]
    at .springframework.batch.core.launch.support.TaskExecutorJobLauncher.run(TaskExecutorJobLauncher.java:146) ~[spring-batch-core-5.2.1.jar:5.2.1]
    at com.batch.job.job.ScheduledJobLauncher.launchJob(ScheduledJobLauncher.java:49) ~[classes/:na]
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
    at .springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) ~[spring-context-6.2.3.jar:6.2.3]
    at .springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) ~[spring-context-6.2.3.jar:6.2.3]
    at io.micrometer.observation.Observation.observe(Observation.java:498) ~[micrometer-observation-1.14.4.jar:1.14.4]
    at .springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) ~[spring-context-6.2.3.jar:6.2.3]
    at .springframework.scheduling.config.Task$OutcomeTrackingRunnable.run(Task.java:85) ~[spring-context-6.2.3.jar:6.2.3]
    at .springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-6.2.3.jar:6.2.3]
    at .springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:96) ~[spring-context-6.2.3.jar:6.2.3]
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[na:na]
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

2025-03-23T10:50:05.133-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.batch.core.step.AbstractStep         : Step: [sampleStep] executed in 3ms
2025-03-23T10:50:05.133-03:00  INFO 54498 --- [job] [   scheduling-1] o.s.b.c.l.s.TaskExecutorJobLauncher      : Job: [SimpleJob: [name=taskletJob]] completed with the following parameters: [{}] and the following status: [FAILED] in 4ms

SampleTasklet.java

@Configuration
@EnableRetry
@Slf4j
public class SampleTasklet implements Tasklet, InitializingBean {

  @Retryable(maxAttempts=2, backoff=@Backoff(delay=100, maxDelay=300))
  public RepeatStatus execute(StepContribution contribution,
                              ChunkContext chunkContext) throws Exception {
    log.debug("Executing Tasklet...");

    int randomNum = RandomGenerator.getDefault().nextInt(0, 10);
    log.debug("Random: " + randomNum);

    if ((randomNum & 1) != 0) {
      log.debug("Odd number");
      throw new IllegalStateException("Failing job...");
    }

    int delay = 1000;
    log.debug("Delaying " + delay + "ms");
    Thread.sleep(delay);

    log.debug("Tasklet executed!");
    return RepeatStatus.FINISHED;
  }

  public void afterPropertiesSet() throws Exception {}
}

JobConfig.java

@Configuration
public class JobConfig {

  @Bean
  public Job taskletJob(JobRepository jobRepository, Step step) {
    return new JobBuilder("taskletJob", jobRepository)
      .start(step)
      .build();
  }

  @Bean
  public Step sampleStep(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
    return new StepBuilder("sampleStep", jobRepository)
      .tasklet(new SampleTasklet(), transactionManager)
      .build();
  }
}

ScheduledJobLauncher.java

@Configuration
@EnableScheduling
@Slf4j
public class ScheduledJobLauncher {
  @Autowired
  JobLauncher jobLauncher;

  @Autowired
  Job job;

  @Autowired
  private ExpirableLockRegistry lockRegistry;

  @Value("${job.lock.registryKey}")
  private String registryKey;

  @Value("${job.lock.maxWaitTime}")
  private Integer maxWaitTime;

  @Scheduled(cron = "${job.scheduling.cron}")
  public void launchJob() throws Exception {
    Lock lock = lockRegistry.obtain(registryKey);
    log.debug("Lock: " + lock.toString());

    boolean lockAcquired =  lock.tryLock(maxWaitTime, TimeUnit.SECONDS);
    log.debug("Lock Acquired: " + lockAcquired);

    if (!lockAcquired) {
      throw new Exception("Lock not acquired");
    }

    try {
      jobLauncher.run(job, new JobParameters());
    } finally {
      lock.unlock();
    }
  }
}

BatchConfig.java

@Configuration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class BatchConfig {
  @Bean
  public PlatformTransactionManager transactionManager() {
    return new ResourcelessTransactionManager();
  }

  @Bean
  public JobRepository jobRepository() {
    return new ResourcelessJobRepository();
  }

  @Bean
  public JobLauncher jobLauncher(JobRepository jobRepository) {
    TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
    jobLauncher.setJobRepository(jobRepository);
    return jobLauncher;
  }
}
Share Improve this question asked Mar 23 at 14:03 Lucas GuimaLucas Guima 318 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

The tasklet is called by Spring Batch, not by (the proxy created by) Spring Framework, so that won't work. You need to use the programmatic way with a RetryTemplate inside the tasklet.

本文标签: How to retry a Spring Batch tasklet using Spring RetryStack Overflow