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

Doc: AbstractMessageListenerContainer rejecting messages on shutdown causes message loss in AUTO_ACK mode [SPR-16487] #21030

Closed
spring-issuemaster opened this issue Feb 12, 2018 · 7 comments

Comments

@spring-issuemaster
Copy link
Collaborator

@spring-issuemaster spring-issuemaster commented Feb 12, 2018

hezhou opened SPR-16487 and commented

The property acceptMessagesWhileStopping in AbstractMessageListenerContainer has defult value false, which will reject messages when shutdown and causes message lossing for AUTO_ACK.
But JMS1.1 tutorial section 4.5.2 specify :
The result of a listener throwing a RuntimeException depends on the session's
acknowledgment mode.
• AUTO_ACKNOWLEDGE - the message will be immediately redelivered.
The number of times a JMS provider will redeliver the same message before
giving up is provider dependent.


Affects: 4.3.14

Attachments:

Referenced from: commits d60446a, 95f7180

Backported to: 4.3.15

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 12, 2018

Juergen Hoeller commented

If you care about message loss, I'd generally recommend transacted JMS sessions. Any form of non-transactional acknowledgment may lose messages (after a number of retry attempts) and is only really meant to be used with notifications in event queues and topic subscriptions. I don't see AUTO_ACK as a production setting for task-oriented queues.

So from AbstractMessageListenerContainer's perspective, this works as designed. The setAcceptMessagesWhileStopping javadoc is quite explicit about this, and its default works pretty well for transacted queue sessions and also for non-transacted topic subscriptions. Otherwise, nothing stops you from setting that flag to true...

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 13, 2018

hezhou commented

Thanks for reply.
But why not throw the exception to Jms Provider, depends on its behavoir.
Catch the MessageRejectedWhileStoppingException and just wan a log does not accord JMS tutorial. For jms provider,it considers user has dealed with this message successfully,but for user , his code (in OnMessage methd) did not see this message.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 13, 2018

hezhou commented

I mean Asynchronous ways : Jms1.1 tutorial excactly specify when ack should happens in AUTO_ACK module

Asynchronous: acknowledgement happens right after the “onMessage” method returns successfully after invoking consumer’s MessageListener. Thus if JMS crashes after “onMessage” completed but before the acknowledgement was recorded, a duplicate of the message will redeliver.

So if user‘s “onMessage” method not completed or throw runtimeException ,JMS provider will guarantee the message not loss.

Thus why not throw the MessageRejectedWhileStoppingException exception to Jms Provider, depends on its behavoir.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 13, 2018

Juergen Hoeller commented

I see your point but unfortunately that part of JMS is not as well-specified as it may seem. JMS providers react to exceptions from MessageListener callbacks quite inconsistently, and they're allowed to do so according to the JMS specification:
"It is possible for a listener to throw a RuntimeException; however, this is considered a client programming error. (...) JMS providers should flag clients with message listeners that are throwing RuntimeException as possibly malfunctioning."

Another noteworthy difference is between SimpleMessageListenerContainer (which is quite provider-dependent in its behavior) and DefaultMessageListenerContainer (with very flexible and configurable runtime behavior): In the SimpleMLC case, we could theoretically propagate exceptions to the provider. However, in the DefaultMLC case, this is not even an option since we're using plain MessageConsumer.receive calls there where AUTO_ACKNOWLEDGE doesn't provide any recovery semantics.

For that reason, Spring's message listener containers never propagate exceptions to the JMS provider. Instead, they offer a CLIENT_ACKNOWLEDGE mode with consistent Spring-driven acknowledgment or - recommended - the use of transacted sessions, both of which work consistently across SimpleMessageListenerContainer and DefaultMessageListenerContainer across all common JMS providers.

All in all, AUTO_ACKNOWLEDGE is just a best-effort mode with inconsistent runtime semantics. If you care about message loss, by any means, please use transacted sessions or at least configure your message listener container with the CLIENT_ACKNOWLEDGE mode.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 14, 2018

hezhou commented

Thanks a lot.
DefaultMLC uses Consumer.receive() method, it is ok to do so . But for SimpleMLC , should we throw the MessageRejectedWhileStoppingException exception to Jms Providers?

In code , just override the handleListenerException(AbstractMessageListenerContainer) method in SimpleMessageListenerContainer class , and its docs also suggests to be overridden.

We hope Spring's default usage and config is enough for users, and not need users to do such more.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 14, 2018

Juergen Hoeller commented

We opted for consistent behavior between our message listener container variants here, and also for defensiveness against different JMS providers. See my quote from the JMS specification above: A MessageListener throwing any exception to the JMS provider (when set through Session.setMessageListener) should be considered as a "programming error", flagging the affected consumer as "malfunctioning". That alone is a reason why our standard message listener containers should never propragate such an exception to the JMS provider: not if a listener fails to process a message and not if we reject a message on shutdown.

The hint at "immediate redelivery" in the spec is not helpful for our purposes either: We don't want immediate redelivery for regular processing exceptions and we don't want them on shutdown either. Instead, we want an unprocessable message to go back to its queue and be redelivered to a different consumer later on, ideally without increasing its redelivery count. There are no reliable semantics for AUTO_ACKNOWLEDGE across providers in such a scenario, so we don't want to bake any such assumptions into Spring's message listener containers and rather aim for consistency between DefaultMLC and SimpleMLC.

All in all, I stand by my recommendation:

  • The only proper solution for avoiding message loss is the transacted session mode: with an individual rollback per unprocessable message during normal operations, and with the same individual rollback for a rejected message on shutdown. Most JMS providers are capable of running very efficiently in this mode of operation.
  • AUTO_ACKNOWLEDGE is only meant for non-durable topic subscriptions and for queues with temporary data. If message rejection on shutdown is to be avoided there, you may set the acceptMessagesWhileStopping flag to true but this does not prevent message loss in general. After all, you can't roll back a message during normal operations either.

I've turned this ticket into a documentation task along those lines. While this topic is covered in several places already, it arguably deserves a more prominent note in the reference docs.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Feb 14, 2018

Juergen Hoeller commented

I've added several explicit notes on transacted sessions vs AUTO_ACKNOWLEDGE now: to the reference documentation as well as to the javadoc of the affected annotations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.