Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the expected behaviour of spring @scheduled cron when jobs would overlap?

Tags:

java

spring

cron

I have a job that runs once an hour, and I'm using Spring's @scheduled cron to schedule it.

If the job takes more than an hour, I understand from How to prevent overlapping schedules in Spring? that the next job won't kick off while the first job is running.

But does this mean that it WILL kick off after the first job has completed, or has it missed its chance?

If I have one job that takes 10 hours, will all the missed cron jobs queue up and then execute one by one when the first job completes after 10 hours, or will just the first job run?

Thanks!

like image 947
Bruce Avatar asked Jan 05 '18 13:01

Bruce


3 Answers

By default, execution is blocking and single-threaded, which means they will not run concurrently. If you want jobs to run simultaneously, you can annotate the method as @Async as well. You may also want to look at the different Executors.

If you're using fixedDelay like in the sample question you provided, the next job will only kick off AFTER the current one is over, plus the delay. So if your job takes 10 hours and you have a fixedDelay of 5000, the next job will kick off 5 seconds after the 10 hour one.

If you're using fixedRate then the next scheduled event will be queued up to run, but not skipped, as per the documentation:

If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.

If you are simply using cron, then the jobs will be queued and executed in turn (similar to fixedRate). You can test this with a simple method (below is in Groovy, but can use plain Java too):

    int i = 0

    @Scheduled(cron = '* * * * * * ')
    void test() {
        if (i < 5)
            Thread.sleep(10000)

        i += 1
        println '------------------------' + i // No values skipped
    }

You'll note that every number is printed; e.g. no cycle is ever skipped.

like image 114
Igor Avatar answered Nov 06 '22 07:11

Igor


The behaviour of fixedRate and cron is different.

Overlapping jobs are queued for fixedRate (as per the above answer from @Igor).

Overlapping jobs are skipped for cron.

Sample Java code to demonstrate the difference:

int i = 0;
@Scheduled(fixedRate = 5000)
public void test() throws InterruptedException {
    Date start = new Date();
    if (i < 3) Thread.sleep(10000);
    i++;
    System.out.printf("start %TT, finish %TT, i = %s%n", start, new Date(), i);
}

And the output:

start 13:25:30, finish 13:25:40, i = 1
start 13:25:40, finish 13:25:50, i = 2
start 13:25:50, finish 13:26:00, i = 3
start 13:26:00, finish 13:26:00, i = 4
start 13:26:00, finish 13:26:00, i = 5
start 13:26:00, finish 13:26:00, i = 6
start 13:26:00, finish 13:26:00, i = 7
start 13:26:05, finish 13:26:05, i = 8
start 13:26:10, finish 13:26:10, i = 9
start 13:26:15, finish 13:26:15, i = 10

As can be seen, the overlapping jobs are queued and start as soon as the previous one completes, with no 5 second gap.

However, if we use @Scheduled(cron = "*/5 * * ? * *") instead, the output becomes:

start 13:22:10, finish 13:22:20, i = 1
start 13:22:25, finish 13:22:35, i = 2
start 13:22:40, finish 13:22:50, i = 3
start 13:22:55, finish 13:22:55, i = 4
start 13:23:00, finish 13:23:00, i = 5
start 13:23:05, finish 13:23:05, i = 6
start 13:23:10, finish 13:23:10, i = 7
start 13:23:15, finish 13:23:15, i = 8
start 13:23:20, finish 13:23:20, i = 9
start 13:23:25, finish 13:23:25, i = 10

There is always a 5 second gap between the jobs. The overlapping jobs are NOT queued and are skipped.

like image 38
firstmanonmars Avatar answered Nov 06 '22 09:11

firstmanonmars


Overlapping jobs are queued for fixedRate and they are skipped for cron as mentioned by @firstmanonmars

If we want to execute the corn schedulers overlapping to each other without waiting, we can use @Async and @EnableAsync as shown below.

@EnableScheduling
@SpringBootApplication
@EnableAsync

public class TaskSchedulerApplication {

    public static void main(String[] args) {
        SpringApplication.run(TaskSchedulerApplication.class, args);
    }
    
    @Bean
    public TaskScheduler taskScheduler() {
        final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(10);
        return scheduler;
    }
}

Scheduler Demo:

@Component
public class DemoScheduler {
    
    @Async
    @Scheduled(cron = "*/5 * * * * MON-FRI")
    public void startJob() {
        System.out.println(String.format("%s - Thread name - %s ",new Date(), Thread.currentThread().getName()));
        sleep(6000);
    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Running the code gives the following output which shows that they are running by multiple threads in parallel in an interval of 5 sec:

Thu May 06 09:59:05 IST 2021 - Thread name - task-1 
Thu May 06 09:59:10 IST 2021 - Thread name - task-2 
Thu May 06 09:59:15 IST 2021 - Thread name - task-3 
Thu May 06 09:59:20 IST 2021 - Thread name - task-4 
Thu May 06 09:59:25 IST 2021 - Thread name - task-5 
Thu May 06 09:59:30 IST 2021 - Thread name - task-6 
Thu May 06 09:59:35 IST 2021 - Thread name - task-7 
Thu May 06 09:59:40 IST 2021 - Thread name - task-8 
Thu May 06 09:59:45 IST 2021 - Thread name - task-1 
Thu May 06 09:59:50 IST 2021 - Thread name - task-2 
Thu May 06 09:59:55 IST 2021 - Thread name - task-3
like image 2
Bhupendra Bhoi Avatar answered Nov 06 '22 09:11

Bhupendra Bhoi