-
Notifications
You must be signed in to change notification settings - Fork 38.4k
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
LifecycleGroup concurrent start and start timeout #34634
Comments
On a related note: As of 6.2, the framework comes with a first-class option for background bootstrapping: #19487. However, that's at the bean creation level, not at the lifecycle start/stop level which comes as a final step in application startup. That background bootstrap mechanism is rather about using multiple CPU cores for expensive configuration and bean construction efforts on startup. It's interesting to see that there is also a benefit in concurrent invocations of |
I've considered it, but wouldn't asynchronous initialization defeat the purpose of having Lifecycle phases? I assume that if component B initializes in a later phase than component A, it can rely on it having completed it's initialization, which wouldn't be guaranteed without a callback from A saying it's done My goal in binding bean initialization to the context's lifecycle is to guarantee the integrity of the app. If a bean fails to I was thinking that "ready-flag" bean could be declared as the sole bean of the last lifecycle phase and serve as an indicator to a |
Sounds sensible. I don't think there is anything wrong with this approach, and we can certainly open up Can you elaborate a bit on the behavior that you are currently implementing in a custom |
Nothing fancy, I copied the existing The core of my changes are in
The only changes to the containing
Keep in mind this is code from my proof on concept and so may not cover all the bases. It ran ok when I tested it, both beans Notably missing here are the boolean flags to enable parallel operation per phase, which I do not require. Cursory analysis makes me think that making I'm happy to keep discussing this but I'm sure you have preferences about how this should be done so I'll let you go first. |
Includes a demonstration ConcurrentLifecycleProcessor class As per spring-projectsgh-34634
Includes a demonstration ConcurrentLifecycleProcessor class As per spring-projectsgh-34634
A proof of concept about opening up Note that this is not a proposition to make |
On review, I'm actually in favor of adding this capability to There is intentionally no general One more important note: We never use the JVM's default common pool in framework facilities. As a consequence, for an actual concurrent startup, a bootstrap I assume this would work for your purposes? It should also make sense for applications that are fine-tuning their concurrent startup through Spring Framework 6.2's background bootstrapping already, letting them opt into concurrency for certain lifecycle phases as well. As straightforward as I have it here, I'd be happy to roll this into our 6.2.6 release in April (making it into Boot 3.5). |
Yes, this would certainly work for us and seems general purpose enough to be useful to others. Reusing the bootstrap executor makes sense. One note: The main feature here is (IMO) the parallel startup, with the timeout being an added safety constraint. I might reflect this in the method name, e.g. Thank you. |
Good point, trying to model this after the shutdown phase timeouts creates a bit of a false impression since it is not actually symmetric indeed (as also indicated by the lack of a |
@fralalonde feel free to give this an early try against the latest 6.2.6 snapshot, just to make sure that it will actually work for your scenario... |
@jhoeller After testing, I confirm this new version fulfills my expectations:
|
Great to hear! Thanks for the pre-release testing. |
Context
Our app communicates with multiple (physical) machines, each being driven by a separate bean. When the app starts, each driver bean resets its machine to a known state. This reset can take a few minutes before which the rest of the app (UI, etc) should not be available. The driver beans implement SmartLifecycle, hardware reset is performed on start().
All machines can be reset at the same time, so to minimize startup time I'm currently using a custom
LifecycleProcessor
forked off theDefaultLifecycleProcessor
, for which I've implemented concurrent start for beans within the same phase (inLifecycleGroup::start()
). This makes theLifecycleGroup
start take as long as the slowest machine's reset, rather than sequentially resetting each one which would be unnecessarily slow.Moreover, I've duplicated the existing per-phase (stop)
timeout
parameter to a start timeout, to make sure the app fails if starting takes more than some expected limit rather than remain in start limbo forever.Proposal
My main assumption is that situations where Lifecycle beans depend on external resources happen frequently, not just in our case. Spring applications could gain both in performance and resilience by making it easier to use concurrent start / stop operations.
To do so would involve either:
.setConcurrentStart(phase, true)
)OR
LifecycleGroup
class with a custom class to allow implementing aforementioned concurrent start mechanism without having to duplicate the entireDefaultLifecycleProcessor
.Thoughts
Beans within the same phase should be safe to start concurrently, since by design they should not have inter-dependencies. While I know this is true for my app, this is not a part of the current
Lifecycle
contract and thus the default should remain sequential, non-timeout start.The underlying support for concurrency could vary a lot from case to case (virtual threads? limit number of concurrent processes?), so maybe the extendable
DefaultLifecycleProcessor
option is more interesting.The text was updated successfully, but these errors were encountered: