In our application, we want to log the changes on the application's readiness state. For this purpose, we created a spring bean with a method with the following signature:
@EventListener
public void onReadinessEvent(AvailabilityChangeEvent<ReadinessState> event) {
// Issue log messages depending on the state
}
If the state is REFUSING_TRAFFIC we issue a WARN message which would draw attention of the operation unit.
However, we'd like to avoid this message if the application is in the course of graceful shutdown -- because then it's normal that the traffic is not accepted. Hence we'd like to be notified about the fact that the graceful shutdown has been initiated. This notification should come before the traffic is not accepted (which seems to be one of the first actions performed when shutdown is initiated).
The sequence would be as follows:
- SIGTERM is received, Spring initiates shutdown
- Spring sends a notification "shutdown initiated"
- Spring stops to accept traffic
- . . . (further actions of the usual shutdown process)
My question on stackoverflow got no answers hence I assume that this is not possible now.
Comment From: mhalbritter
Could be a useful feature. I flagged it as "team attention" to see what the team thinks about that.
Comment From: bclozel
Hello @fml2, we've discussed team during a team meeting and we have a few questions.
If the state is REFUSING_TRAFFIC we issue a WARN message which would draw attention of the operation unit.
Besides the application being shut down, what are the other cases that can trigger this state change? Is your application declaring itself as "unready" because it's too busy processing a task? Could you share a bit more about this?
However, we'd like to avoid this message if the application is in the course of graceful shutdown -- because then it's normal that the traffic is not accepted.
From the application perspective, there's no way to understand why we received a SIGTERM signal. It could be a rolling upgrade, the platform shutting down extra instances, the platform trying to shut down an instance because it declared its liveness state as broken... Many can be considered as "normal" or "problematic" depending on the perspective. So again, in your case something else might be changing this state outside of the normal shutdown process.
Right after the the REFUSING_TRAFFIC state change, the context should publish a ContextClosedEvent. Could this be a signal that your application could listen to?
Comment From: fml2
If I understand correctly, the readiness state is not tied to the shutdown process (or a SITERM signal) but is rather a result of the readiness test which is configured via the actuator. If some part of the application reports back that it's not ready then the whole application is considered not ready. We'd like to catch this moment so that we can see when the application was first considered as "not ready".
But we're interested only in the cases when the application became "not ready" because of some internal problem and not because of an external SIGTERM or whatever else (which, as you correctly noted, is not distinguishable).
Comment From: bclozel
Thanks for your feedback @fml2
If I understand correctly, the readiness state is not tied to the shutdown process (or a SITERM signal) but is rather a result of the readiness test which is configured via the actuator.
No, the state change is not a result of a healthcheck call in actuator, but an application state change powered by Spring events that components can publish or listen to.
But we're interested only in the cases when the application became "not ready" because of some internal problem and not because of an external SIGTERM or whatever else (which, as you correctly noted, is not distinguishable).
I guess that in those cases, the application itself is publishing the event.
I don't think that adding a new GracefulShutdownStarted event would solve your problem, as your application component would need to keep track of multiple events and wouldn't know why the state changed. In the future, Spring Boot could publish readiness state changes for other reasons if the community requests it.
Here's an example of a component (here a controller) that publishes a readiness state change because of an exception. As you can see, the exception is used as the source of the event:
```java @RestController public class TestController {
private final ApplicationEventPublisher eventPublisher;
public TestController(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@PostMapping("/refuse-traffic")
public String changeReadinessState() {
AvailabilityChangeEvent.publish(this.eventPublisher, new IllegalStateException("application is busy processing a task"), ReadinessState.REFUSING_TRAFFIC);
return "Readiness State changed";
}
}
Event listeners can rely on the source to learn more on what published this event or why.
`ReadinessState` event changes published by Spring Boot use the current `ApplicationContext` instance as the source, since the application context is driving the lifecycle of the web server. You can apply the following pattern right now and have a better tracking of the state changes:
```java
@EventListener
public void onStateChange(AvailabilityChangeEvent<ReadinessState> event) {
if (event.getSource() instanceof ApplicationContext) {
logger.debug("Application context changed Readiness state to {}", event.getPayload());
}
else if (event.getSource() instanceof Throwable throwable) {
logger.warn("Readiness state changed to {} due to {}", event.getPayload(), throwable);
}
}
Please let us know is this approach works out for you.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: fml2
Thank you for the reminder. I'll report back in the next days.
Comment From: fml2
@bclozel , this seems to work as you described. Thank you!