-
Notifications
You must be signed in to change notification settings - Fork 639
Description
In what version(s) of Spring AMQP are you seeing this issue?
The issue appeared after updating Spring Boot from 2.7.13 to 2.7.14, which bumps spring-amq to 2.4.14.
The problem does not show up anymore when reverting (only) the spring-amqp version to 2.4.13.
Describe the bug
When the Spring Boot application shuts down (e.g. via stop button in IntelliJ), it will not immediately shutdown anymore. Instead, there is a delay of 30sec, followed by a Spring error message:
...ecycleProcessor$LifecycleGroup | Failed to shut down 1 bean with phase value 2147483647 within timeout of 30000ms: [org.springframework.amqp.rabbit.config.internalRabbitListenerEndpointRegistry] [stop(), DefaultLifecycleProcessor.java:384]
Expected behavior
Upon application shutdown, all listener containers are either already stopped or will be stopped within a few seconds, so there is no bean shutdown timeout error after 30 sec.
To Reproduce
I guess this could be related to the changes to the shutdown behaviour in 95631d8
I was not able to create a small-scale sample yet, but I've debugged the related Spring code and think I might have found the the reason and a possible solution for the delayed shutdown. See the "sample" below :) However I don't understand why the code worked in earlier versions of spring-amqp.
Analysis
To analyze what is happening, I defined several logging debugger breakpoints in spring-amqp and then triggered an application exit.
These breakpoints log the current method and the queues of the listener container. Here is what`s happening:
shutdown() called in AbstractMessageListenerContainer:1366 with queues: [reporting-service.Replication-4ulJUMIkRnG2K-fG4aDp1g]
-> shutdownAndWaitOrCallback called in SimpleMessageListenerContainer with queues: [reporting-service.Replication-4ulJUMIkRnG2K-fG4aDp1g]
-> stopping consumer: [reporting-service.Replication-4ulJUMIkRnG2K-fG4aDp1g]
-> stopped consumer: [reporting-service.Replication-4ulJUMIkRnG2K-fG4aDp1g]
-> callback executed? true
shutdown() called in AbstractMessageListenerContainer:1366 with queues: [reporting-service.LiveSync]
-> shutdownAndWaitOrCallback called in SimpleMessageListenerContainer with queues: [reporting-service.LiveSync]
-> stopping consumer: [reporting-service.LiveSync]
-> stopped consumer: [reporting-service.LiveSync]
-> callback executed? true
shutdown() called in AbstractMessageListenerContainer:1366 with queues: [reporting-service.SnapshotSync-4ulJUMIkRnG2K-fG4aDp1g]
-> returning because if(!isActive), but not executed the callback although one is defined!
My conclusion:
- there are 3 SimpleMessageListenerContainer beans in the application context (2 active, 1 inactive)
- on app shutdown, Spring`s DefaultLifecycleProcessor creates a countdown latch of 3 and calls shutdown(Runnable callback) on all 3 container beans. The callback function will decrease the latch when executed.
- only the currently active listener containers do execute the callback
- an inactive listener container does not execute the (non-null) callback method before the
return
in AbstractMessageListenerContainer.java:1370. However Spring seems to rely on the callback being executed by shutdown() in any state. - therefore the countdown latch is never decreased to 0, so the timeout exception is raised after 30sec
Just for fun, I made the breakpoint execute the callback function in AbstractMessageListenerContainer.java:1370.
That fixes the problem and the application shuts down immediately.