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

[CRaC] DefaultLifecycleProcessor and stopping beans on first refresh #34510

Open
rvansa opened this issue Feb 27, 2025 · 3 comments
Open

[CRaC] DefaultLifecycleProcessor and stopping beans on first refresh #34510

rvansa opened this issue Feb 27, 2025 · 3 comments
Labels
status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged or decided on

Comments

@rvansa
Copy link

rvansa commented Feb 27, 2025

Hi,

I started hacking a solution that would close Cassandra connections on checkpoint, following [1] and registering a Lifecycle that would let the Cassandra session 'suspend'. The checkpoint and restore works in my prototype when I issue the checkpoint via jcmd <pid> JDK.checkpoint, but if I try automatic checkpoint through -Dspring.context.checkpoint=onRefresh the Lifecycle.stop() is not called: at this point DefaultLifecycleProcessor.running is false and stopForRestart() is not invoked.

I would like to ask what's the reason and if there's any better pattern; [2] explains that the connections are established too early (before first refresh). Is that a show-stopper? We can close those connections and keep going.

An alternative solution would be to not use the Lifecycle and register as a org.crac.Resource; however I understand that it's preferred to integrate the handling into Spring using that rather than directly.

CC @sdeleuze

[1] https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.java
[2] spring-projects/spring-data-cassandra#1486

@jhoeller
Copy link
Contributor

Generally, Lifecycle.stop() is only invoked for components that received a Lifecycle.start() callback before, i.e. only what got started actually needs to be stopped. As long as the activity that you intend to stop in stop() only gets started in start(), you should be fine.

This is also the pattern followed for an onRefresh checkpoint: We aim to not even start the components there yet, in order to avoid the need for explicit stopping to begin with. Only for later checkpoints at runtime, there is an actual need to stop components.

For core framework components, we explicitly have all such activity in start() methods rather than regular init methods for that reason. In that sense, init methods are mirrored by destroy methods (matching bean instance creation/destruction), while start methods are mirrored by stop methods (for dynamic activities that can get started/stopped/restarted in the same bean instances).

@rvansa
Copy link
Author

rvansa commented Feb 28, 2025

@jhoeller Thanks for those answers! The symmetry makes sense - could you make any suggestion for integration of components that don't have a proper lifecycle and do startup as part of bean instantiation? I've checked Cassandra code and the CqlSession (DefaultSession) starts the connections from constructor eagerly, while it serves as a bean, too, so it's instantiated in order to be injected. Not sure about proxy options here. I might be able to override a handful of components to enforce a lazy start, but that feels rather intrusive.

I've placed a few breakpoints into the starting application and it seems that DefaultLifecycleProcessor.start() is not invoked at all (.refresh() is invoked from ApplicationContext <- SpringApplication.run), it becomes running by the refresh, only the SmartLifecycle beans get started as part of the refresh. So effectively this is breaking the concept that a component has to be started to become running.

I've implemented the lifecycle with running = true from the start, which is probably a hack. However since component stopping already checks if Lifecycle.isRunning(), it sounds safe to ignore the status of DLP itself (just a little excessive to go through the beans to see that, in the general case, none is started).

@sdeleuze
Copy link
Contributor

@rvansa Is the CqlSession exposed as a Spring bean by Spring Data/Boot ?

@sdeleuze sdeleuze added the status: waiting-for-feedback We need additional information before we can continue label Feb 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged or decided on
Projects
None yet
Development

No branches or pull requests

4 participants