Skip to content

Regression: ApplicationContext.stop() Hangs for 30s When Inactive Container(s) Present #2511

@lukaseckert

Description

@lukaseckert

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions