Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems using spring @Scheduled

I have three methods on my project annotated with @Scheduled, being one of them a cron expression and the other two are fixed delay. The annotations look like:

Method 1:

@Scheduled(fixedDelay = 20000)
@Async
protected void checkBrokenEngines() {

Method 2:

@Scheduled(fixedRate = 20000)
@Async
public void checkAvailableTasks() throws Exception {

Method 3:

@Scheduled(cron = "0 0 2 * * ?")
protected void deleteOldData() {

Previously I had a problem that when the checkBrokenEngines and checkAvailableTasks methods were slow to be executed, the next executions did not happen until the previous one ended. Reading the documentation and some topics here from the StackOverflow, I saw that my project had some wrong pool size settings and the methods were not annotated with async. (Async was for the next execution start even if the old one doesn't end, as this does not cause any problems in my application)

Now another problem has come up, which is my question:

When the deleteOldData() method is executed, neither of the other two methods will run until it finishes. After seeing that this method was blocking the execution of the other two, I annotated the method as async and after that, even if this method takes time to be executed, the other two are always invoked correctly within the stipulated time. Why? In my understanding, this should not happen since those methods are noted with async and the pool has enough space to execute them.

PS: deleteOldData() cannot be async. It must start just after the previous execution has been completed.

EDIT 1:

Async executor config:

@Override    
public AsyncTaskExecutor getAsyncExecutor() {
    log.debug("Creating Async Task Executor");
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(50);
    executor.setMaxPoolSize(50);
    executor.setQueueCapacity(10000);        
    return new ExceptionHandlingAsyncTaskExecutor(executor);
}
like image 302
Cristiano Bombazar Avatar asked Mar 10 '20 11:03

Cristiano Bombazar


1 Answers

All @Scheduled marked invocations will use the default single thread executor to schedule tasks (async or otherwise).

All @Async tasks are handed off to different aysnc threadpool executor for executions with AOP interceptor.

I think your confusion comes from the fact async methods return immediately but when deleteOldData is run, it is running synchronously as there is only one thread, it blocks the execution of any other scheduled tasks.

Because you have default threadpool (single thread) for the scheduled tasks they are scheduled one after the another.

The other methods annotated with @Async they execute even if it finishes or not. In some cases, I have two methods of executing at the same time. But when deleteOldData is executing, the async methods stop to run until it finishes. This what I'm not understanding, sorry :/ –

This is different from scheduling - This is where your async executor comes into play and they are run concurrently.

You can fix this in one of two ways:

You can use spring.task.scheduling.pool.size=10 in application properties to set the pool size of task scheduler.

Alternatively, use different tasks schedulers. Keep using default scheduler for @Scheduled task and configure something like below for async tasks ( remove scheduled annotation )

There is an enhancement requested to pass task scheduler to the @Scheduled annotation until then you have to schedule tasks manually.

Register a new task scheduler for async invocations and schedule the methods in the post construct stage. Something like

Configuration

@Bean("asyncTaskScheduler")
public TaskScheduler asyncTaskScheduler() {
  return new ThreadPoolTaskScheduler();
}

Services

@Autowired
private TaskScheduler asyncTaskScheduler;

@PostConstruct
void schedule() {
  asyncTaskScheduler.scheduleAtFixedRate(this::checkAvailableTasks, 20000L);
  asyncTaskScheduler.scheduleAtFixedDelay(this::checkBrokenEngines, 20000L);
}

@Async
public void checkBrokenEngines() {...}

@Async
public void checkAvailableTasks() throws Exception {...}
like image 180
s7vr Avatar answered Sep 29 '22 03:09

s7vr