-
Notifications
You must be signed in to change notification settings - Fork 38.3k
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
Graceful shutdown does not cancel @Scheduled tasks #32109
Comments
This is a surprisingly nuanced topic given all the input and feedback we had on this over the years. A key idea behind a graceful shutdown is to let existing tasks complete as far as possible, concurrently in case of multiple executors/schedulers. This was explicitly requested for scheduled tasks (#31019) even before the lifecycle revision in 6.1, and after the lifecycle revision there is dedicated support for such a mode of shutdown now. From that perspective, the behavior that you are experiencing is by design: For a graceful shutdown, we cancel recurring tasks so that further triggers do not fire anymore but let running tasks complete concurrently within the managed stop phase. If your task takes longer than that to complete, could you try to redesign it for shorter but more frequent triggering possibly? Or otherwise, just set a short enough lifecycle timeout and let it run into that info-level log message (which is not meant to be an error - maybe we should avoid the "failed" term there), followed by an interrupt on remaining tasks for a hard shutdown. No need to set any extra flags for this, you could just rely on the default arrangement there and set a custom lifecycle timeout. The (old) The (new) All things considered, I actually recommend the default shutdown behavior with a custom lifecycle timeout, possibly even shorter than 5s. We can revise the wording of that log message if that's the main irritation, e.g. "Shutdown phase 2147483647 ends with 1 bean still running after timeout of 5000ms: [taskScheduler]". |
Thanks for the detailed response. Your suggested change of log output is already helpful with clearing up the confusion. taskScheduler.setAcceptTasksAfterContextClose(true);
taskScheduler.setAwaitTerminationMillis(0); Neither the names nor the documentation show that these two methods are in any way related. The first call in particular does not seem to have any effect on already running tasks. In contrast, the While changing the API to be more intuitive might be tricky due to backwards compatibility, I would suggest clearing up the documentation of these methods. Thanks again. |
I've spoken too soon about knowing how to handle my use-case. For a bit more context, I have a project with a graceful shutdown so that active requests are still completed (within the timelimit). I also have a series of tasks running, some of them via I've tried to create two taskSchedulers: one "normal" one and one with these lines: taskScheduler.setAcceptTasksAfterContextClose(true);
taskScheduler.setAwaitTerminationMillis(0); I use the latter one in the scheduled task that should be canceled immediately. When shutting down the application and when no other task is running, or request is being completed, the task is canceled immediately, just as I need it to. However, when either another scheduled task is running (with the "normal" taskScheduler) or a long running request is being completed, my special task is only being canceled when the other task or request is completed or times out. Thus it seems to me that How can I get one taskScheduler to cancel its tasks immediately while still retaining the graceful shutdown for other taskSchedulers and endpoints? I feel this question may no longer be appropriate in an issue and should move to a discussion, but I am not sure the described behavior is intended. |
Thanks for sharing your scenario there, this is useful insight. All of this input is useful for revising our documentation there. Some of those configuration options have legacy behind them. We try to keep them intact for backwards-compatible behavior in existing applications and also for enforcing pre-6.1 behavior in new setups if necessary. The name of the setting often reflects the original purpose but the overall semantics are not very obvious indeed. Also, please note that those setter methods only affect the local TaskScheduler instance; other TaskScheduler instances operate independently according to their own configuration. If certain tasks go through graceful stopping on one scheduler, that lifecycle step happens before any beans - including other schedulers - reach their destroy step; that's a consequence of the unified lifecycle model. As for your special task, you could try to specifically react to a |
…er ThreadPoolTaskScheduler (#2721) (#2738) There has been a significant revision of `ThreadPoolTaskScheduler/Executor` lifecycle capabilities as part of the spring-6.1.x release. It includes a concurrently managed stop phase for `ThreadPoolTaskScheduler/Executor`, favouring early soft shutdown. As a result, the `ThreadPoolTaskScheduler`, which is used for pubsub publishing, now shuts down immediately on `ContextClosedEvent`, thereby rejecting any further task submissions (#2721). This PR aims to retain the default behavior of spring-6.1.x but provides config options to leverage the `lateShutdown` of the underlying `ThreadPoolTaskScheduler`, such as: `spring.cloud.gcp.pubsub.publisher.executor-accept-tasks-after-context-close=true`. References: 1. spring-projects/spring-framework#32109 (comment) 2. spring-projects/spring-framework@b12115b 3. spring-projects/spring-framework@a2000db 4. spring-projects/spring-framework#31019 (comment) 5. https://github.com/spring-projects/spring-framework/blob/996e66abdbaad866f0eab40bcf5628cdea92e046/spring-context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java#L482 Fixes #2721.
Steps to reproduce:
@Scheduled(fixedRate=1000)
server.shutdown=graceful
andspring.lifecycle.timeout-per-shutdown-phase=5s
If I understand the docs correctly, the long running process should be canceled immediately and the task scheduler should be destroyed.
However, when signaling the application to shutdown, the long running process is not aborted immediately. Instead, I get an error message after 5 seconds: Failed to shut down 1 bean with phase value 2147483647 within timeout of 5000ms: [taskScheduler]
If I configure the taskScheduler with
the task is canceled immediately.
Minimal example
The TaskScheduler's destroy method and the predestroy() method are not called until after the 5 second timeout.
If I configure the taskScheduler with setWaitForTasksToCompleteOnShutdown(true) and taskScheduler.setAwaitTerminationMillis(0), these methods are called immediately.
Is there a misunderstanding on my part, an error in the docs, or a bug?
The text was updated successfully, but these errors were encountered: