Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

This page is under review for accuracy

Message Acknowledgements and Delivery Modes

...

In this mode, the client acknowledges each message once it has been received by the user. In the case of an asynchronous message consumer, this means that an acknowledgement is sent once the onMessage method of a message listener has completed without throwing an exception of any sort. For a synchronous consumer, it means when the receive() method has returned the message to the user.In this mode, each HANDLE NOTIFY sent from the server to the client will result in one CHANNEL ACK being sent from the client to the server

A single BasicAckBody is sent with the delivery tag of the message and the multiple flag set to false, acknowledging that message only.

CLIENT_ACKNOWLEDGE (JMS)

In this mode, the user acknowledges messages manually by calling the acknowledge() method . The user does not have to acknowledge each message therefore the user can decide on a batching strategy in order to reduce network trafficon either the session or the message itself. These both have the same effect.

The JMS does not say how many message a client is allowed to receive before acknowledging. However, it does talk in vague terms about implementations making sure clients don't go too long without acknowledging to avoid resource exhaustion. Qpid uses the prefetch value for this - the consumer must ack it's messages before it reaches this limit if it wants to recieve any more.

Calling either Session.acknowledge or Message.acknowledge() introduces a "prefetch" which specifies, for a particular destination, how many messages the client can read without sending an acknowldgement. The method Session.setDefaultPrefetch(int) allows a default value to be configured at the session level and an overloaded createConsumer() method exists allowing it to be specified at the point of consumer construction.Calling Message.acknowledge() sends a CHANNEL ACK to be sent from the client to the server. This acknowledges the receipt of all messages up to and including this the current one.

DUPS_OK_ACKNOWLEDGE (JMS)

In this mode, the client acknowledges message receipt as in the case of AUTO_ACKNOWLEDGE but is not obliged to acknowledge each message immediately upon successful completion of the onMessage() or receive() methods.

This means that the client can have an "acknowledgement strategy" that can provide higher performance at the expense of potential redelivery of messages in the event of a failure.

Qpid provides a default configurable strategy parameterised on number of messages received and elapsed time since last acknowledgement. The user can configure the client to send acknowledgements every n messages (where n <= the prefetch value for the consumer) or every k milliseconds whichever is reached sooner. Values of zero for either n or k means that component of the trigger is disabled. Setting n = 0 and k = 0 results in behaviour identical to CLIENT_ACKNOWLEDGE. Setting n = 1 and k = 0 results in behaviour identical to AUTO_ACKNOWLEDGEThis mode is identical to AUTO_ACKNOWLEDGE from an implementation perspective, however the user application must be prepared to deal with duplicate messages.

PRE_ACKNOWLEDGE (non-JMS)

A mode not covered by the JMS specification is one where the client acknowledges a message before calling the onMessage() or receive() methods. This method results in the server receiving one CHANNEL ACK per HANDLE NOTIFY as in AUTO_ACKNOWLEDGE but the difference is that the ack will/can(? - this choice could cause a 'DUP' effect) be sent even if user code can throws an error.

This is useful where the user does not want to trigger redelivery of a message if user code fails or where synchronous operation is used and it is expected that there is some delay before receive() is called (which would in turn cause the server to have to wait some time before receiving the ack).

It sends a BasicAcknowledge for each message as the message is passed to onMessage or receive() retrives it. The semantics are therefore exactly the same as AUTO_ACKNOWLEDGE for receive(), but differ for onMessage() in that the message is acknowledged regardless of whether the method completes successfully or not.

The constant org.apache.qpid.jms.The constant Session.PRE_ACKNOWLEDGE defines this mode.

...

Setting NO_ACKNOWLEDGE means that the client never sends a BasicAcknowledge and the broker removes the message from the queue as soon as it is sent.

The constant org.apache.qpid.jms. needs to send CHANNEL_ACK messages. The constant Session.NO_ACKNOWLEDGE defines this mode.

...

For message production, similar considerations apply. JMS defines two delivery modes, PERSISTENT and NON_PERSISTENT which allow the implmentor implementor considerable freedom of implementation.

Unfortunately the JMS specification addresses what are really two separate reliability concerns with a single delivery mode. OpenAMQ adds an additional mode to give the user the ability to have finer control over the tradeoff between performance and reliability.

The default delivery mode can be set on a producer. This can be overridden on each message sent.

...

Persistent is the straightforward option. Messages are sent with the persistent flag set to true which means that they will be committed to stable storage. HANDLE SEND frames are sent with the confirm tag set to true. When the client receives the HANDLE REPLY frame it knows that the message has been committed to stable storage on the broker.

We need to clarify with Pieter what confirm tags really mean. Do they mean pure physical receipt of the message or receipt & completion of all necessary processing? The semantics of 'processing' will vary from message-type to message-type. For example would 'HANDLE SEND' mean delivery to all end points (clients)... unlikely... or delivery to all the data-structures - queues, topics etc, or just delivery to the mux?

In this particular case 'processing' would probably mean writing to some persistent storage. Whatever the 'rule', it should be unambiguous, understood by all developers and enforced. Perhaps a 'processed' flag could be associated with the tag to indicate that it should only be generated when processing has also completed? Alternatively this could be a field that can optionally piggy-back in the confirm tag indicating that the confirm tag is also acknowledgement of completion of all necessary processing. If it does not contain that flag, then a separate acknowledgement message may be generated some time later. This gives the broker some implementation freedom.

The MessageProducer.send() method blocks until the HANDLE REPLY has been received therefore the user can know that a message has been reliably sent to the broker.

NON_PERSISTENT (JMS)

Non persistent gives maximum performance with least guarantees. The persistent flag is set to false in each message which means that if the broker dies all messages that have no been delivered at that point are lost irretrievably.

Also no acks are requested in the HANDLE SEND. Acks are pointless in this case since after sending the ack the server could die and the message would be lost.

PERSISTENT_GROUPACK (non-JMS)

An ack means that the client knows that the last message it sent was received successfully. However, acks have a considerable performance overhead. A client may want to be able to send message quickly but also have the guarantee that if the broker dies the messages will not be lost.

PERSISTENT_GROUPACK means that messages are written to stable storage, which incurs an overhead, but the client only requests an ack every n messages. The Session.setGroupAckThreshold() method allows the client to specify the ack frequency.

Note that this is different to a transacted commit since in this mode, messages can be delivered to client before the group-ack. Transacted messages are not available to clients until the commit.

TODO: define some API to allow the client to determine the set of mesages not acked in the event of failuresuffers an error it is neither required nor expected to recover those messages.