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
1 Answer
Reset to default 0The 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.