Ben Spiller
Ben Spiller

Reputation: 535

Anyone know exactly which JMS messages will be redelivered in CLIENT_ACKNOWLEDGE mode if the client crashes?

The spec says "Acknowledging a consumed message automatically acknowledges the receipt of all messages that have been delivered by its session" - but what I need to know is what it means by 'delivered'.

For example, if I call consumer.receive() 6 times, and then call .acknowledge on the 3rd message - is it (a) just the first 3 messages that are ack'd, or (b) all 6?

I'm really hoping it's option a, i.e. messages after the one you called acknowledge on WILL be redelivered, otherwise it's hard to see how you could prevent message lost in the event of my receiver process crashing before I've had a chance to persist and acknowledge the messages. But the spec is worded such that it's not clear. I get the impression the authors of the JMS spec considered broker failure, but didn't spend too long thinking about how to protect against client failure :o(

Thanks Ben

Upvotes: 3

Views: 2649

Answers (1)

T.Rob
T.Rob

Reputation: 31832

According to the spec, option (b) is the correct behavior. If you are getting option (a) and rely on it, the application will not be portable.

In the explanation below, I'm referring specifically to the JMS spec Version 1.1 April 12, 2002.

There is some confusion caused by the fact that the acknowledge method is called from the message object when in fact it operates at the session level. Because it is a message method, intuitively it seems correct that one could pick a point in the message stream at which to generate an acknowledgment.

What is really happening though is that message acknowledgment is driving commit calls at the session level. Since a session can have only one transaction active at a time, each acknowledgment delimits not a point in the message stream but rather a point in time. The ack commits the existing unit of work and starts the next. Messages delivered prior to the ack must be included in the unit of work that was committed by the ack.

The term "delivered" is generally thought of as completion of the API call that removes the message from the queue and results in a populated object in the program's memory. In reality, the message is considered delivered when it is removed from the queue, regardless of whether it makes it to the program. For example, consider the following sequence of events:

  1. The app requests a message.
  2. The request is passed over a TCP socket to a process on the server which acts as a proxy for the application.
  3. The proxy issues the GET against the queue.
  4. The message is locked in a unit of work and passed to the proxy process.
  5. The proxy process attempts to deliver the message over a TCP socket to the calling application. If the connection is severed at this point, the application will not have ever seen the message but the JMS provider thinks it has been delivered. When the broken connection is detected, the unit of work is canceled, the message is rolled back onto the queue and the redelivery count is incremented.
  6. The application receives the message.
  7. The application acknowledges the message.
  8. The proxy process receives the commit call and executes it.

There is a window between 6 & 8 during which the TCP socket can be disconnected. On the JMS provider side, it can't really distinguish between this and failure at Step 5. Either way, the message is rolled back and redelivered later. However in this case the application will see the message twice. The spec anticipates this situation in 4.4.13 where it states:

If a failure occurs between the time a client commits its work on a Session and the commit method returns, the client cannot determine if the transaction was committed or rolled back. The same ambiguity exists when a failure occurs between the non-transactional send of a PERSISTENT message and the return from the sending method.

It is up to a JMS application to deal with this ambiguity. In some cases, this may cause a client to produce functionally duplicate messages.

A message that is redelivered due to session recovery is not considered a duplicate message.

In your example where you "call consumer.receive() 6 times, and then call .acknowledge on the 3rd message" and are observing that messages 4 through 6 are redelivered, the possible explanations are that a) the six messages are not all from the same session, or b) the behavior is not compliant with the spec.

Upvotes: 4

Related Questions