Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

With virtual threads enabled, separate fixed-delay tasks cannot run concurrently #31900

Closed
kzander91 opened this issue Dec 23, 2023 · 2 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: documentation A documentation task
Milestone

Comments

@kzander91
Copy link
Contributor

kzander91 commented Dec 23, 2023

Framework: 6.1.2
Boot: 3.2.1

With spring.threads.virtual.enabled=true and otherwise default config, all methods annotated with @Scheduled(fixedDelay = ...) are executed on the same virtual thread, preventing multiple scheduled methods from running at the same time. cron and fixedRate tasks are not limited like this and run on separate threads (but of course allow executions of the same task to overlap).
The documentation states:

There is also a new option called SimpleAsyncTaskScheduler which is aligned with JDK 21’s Virtual Threads, using a single scheduler thread but firing up a new thread for every scheduled task execution.

This behaviour is most likely due to #31334, but shouldn't separate fixed-delay tasks still be allowed to run in parallel?
The following demonstrates this behaviour:

@Slf4j
@EnableScheduling
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Scheduled(fixedDelay = 1000)
    public void sched1() throws InterruptedException {
        log.info("Starting sched1, sleeping for five seconds. Thread: {}", Thread.currentThread());
        TimeUnit.SECONDS.sleep(5L);
        log.info("Done with sched1.");
    }

    @Scheduled(fixedDelay = 1000)
    public void sched2() throws InterruptedException {
        log.info("Starting sched2, sleeping for five seconds. Thread: {}", Thread.currentThread());
        TimeUnit.SECONDS.sleep(5L);
        log.info("Done with sched2.");
    }

}

This produces output like this, showing that even while one task is blocked, the other one has to wait:

2023-12-23T22:34:08.092+01:00  INFO 23560 --- [   scheduling-1] com.example.demo.DemoApplication         : Starting sched1, sleeping for five seconds. Thread: VirtualThread[#50,scheduling-1]/runnable@ForkJoinPool-1-worker-4
2023-12-23T22:34:13.093+01:00  INFO 23560 --- [   scheduling-1] com.example.demo.DemoApplication         : Done with sched1.
2023-12-23T22:34:13.093+01:00  INFO 23560 --- [   scheduling-1] com.example.demo.DemoApplication         : Starting sched2, sleeping for five seconds. Thread: VirtualThread[#50,scheduling-1]/runnable@ForkJoinPool-1-worker-4
2023-12-23T22:34:18.094+01:00  INFO 23560 --- [   scheduling-1] com.example.demo.DemoApplication         : Done with sched2.

Is this behaviour intentional? If so, I would have expected a note about this in the docs...

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 23, 2023
@jhoeller jhoeller self-assigned this Dec 23, 2023
@kzander91 kzander91 changed the title With virtual threads enabled, separate fixed-delay taks cannot run concurrently With virtual threads enabled, separate fixed-delay tasks cannot run concurrently Dec 24, 2023
@jhoeller
Copy link
Contributor

This is intentional since the effort of managing multiple scheduler threads just for the potential concurrent execution of multiple fixed-delay threads seems not worth the overhead.

Effectively, fixed-delay semantics are discouraged with virtual threads since you do not get a thread-per-task handoff there in any case. This is currently just being mentioned in the SimpleAsyncTaskScheduler javadoc:

"NOTE: Scheduling with a fixed delay enforces execution on the single scheduler thread, in order to provide traditional fixed-delay semantics! Prefer the use of fixed rates or cron triggers instead which are a better fit with this thread-per-task scheduler variant."

I understand that this is not very obvious. Where would you expect it to be mentioned so that it is easier to find?

@jhoeller jhoeller added type: documentation A documentation task in: core Issues in core modules (aop, beans, core, context, expression) and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Dec 24, 2023
@jhoeller jhoeller added this to the 6.1.3 milestone Dec 24, 2023
@kzander91
Copy link
Contributor Author

I see, thank you.
I would have expected this in the Javadoc of the @Scheduled annotation, and somewhere in the task scheduling section of the reference docs I linked above.

This is probably also an issue with Boot's documentation, because the task scheduling properties also don't mention this limitation. spring.task.scheduling.simple.concurrency-limit is -1 by default, with its description stating "-1 indicates no concurrency limit at all.", but actually, fixed-delay tasks are subject to a concurrency limit of 1 when virtual threads are enabled.

roman-struchev added a commit to roman-struchev/realitica-parser that referenced this issue Feb 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: documentation A documentation task
Projects
None yet
Development

No branches or pull requests

3 participants