Here we describe the broker semantics. This should be read together with the AMQP specification.
The semantics of AMQP’s tx class, as defined in AMQP 0-9-1, and its implementation in different versions of the RabbitMQ server, is often misunderstood. Here is a summary of the behaviour:
|Feature||AMQP0-9-1||RabbitMQ< 2.6.0||RabbitMQ2.6.0-2.7.1||RabbitMQ>= 2.8.0|
|transactional exchange/queue/binding creation/deletion||no||no||no||no|
|transactional consuming/getting of messages||no||no||no||no|
|atomicity in single queue||yes||no||no||no|
|atomicity across multiple queues||no||no||no||no|
|error detection (e.g. invalid exchange)||undefined||immediate||immediate||immediate|
|sending of ‘no_route’ basic.return||undefined||immediate||on commit||on commit|
|effect visibility / responsibility transfer / durability||undefined||on commit||on commit||on commit|
Overall the behaviour of the AMQP tx class, and more so its implementation on RabbitMQ, is closer to providing a 'batching’ feature than ACID capabilities known from the database world.
AMQP transactions only apply to publishes and acks. We have additionally made rejection transactional. Other operations such as resource creation/deletion are not transactional. Consequently the behaviour of transactions when any of the involved exchanges, queues or bindings are altered is undefined.
On the consuming side, the acknowledgements are transactional, not the consuming of the messages themselves. Hence no requeuing of consumed messages takes place on rollback; the client can still ack/reject these messages in subsequent transactions.
AMQP guarantees atomicity only when transactions involve a single queue, i.e. all the publishes inside the tx get routed to a single queue and all acks relate to messages consumed from the same queue. When multiple queues are involved it is possible that in the event of a broker failure during tx.commit the effects of the transaction are only visible in some of the queues. Furthermore, RabbitMQ provides no atomicity guarantees even in case of transactions involving just a single queue, e.g. a fault during tx.commit can result in a sub-set of the transaction’s publishes appearing in the queue after a broker restart.
AMQP does not specify when errors (e.g. lack of permissions, references to unknown exchanges) in transactional basic.publish and basic.ack commands should be detected. RabbitMQ performs the necessary checks immediately (rather than, say, at the time of commit), but note that both basic.publish and basic.ack are asynchronous commands so any errors will be reported back to the client asynchronously.
The situation is similar with basic.returns, though note the slight change in behaviour between earlier and recent versions of RabbitMQ. You will always receive any basic.returns before the tx.commit-ok.
AMQP does not specify when the effects of transactions should become visible following a tx.commit, e.g. when published messages will appear in queues and can be consumed from other clients, when persistent messages will be written to disk, etc. In RabbitMQ the tx.commit-ok indicates that all transaction effects are visible and that the broker has accepted responsibility for all the messages published in the transaction.
For acknowledgements, the receipt of a tx.commit-ok is an indicator that the acknowledgements have been received by the server, not that they have been processed, persisted, etc. Consequently it is possible for a subsequent server-side failure to “resurrect” the acknowledged messages, and for consuming clients to receive them again.
Section 4.7 of the AMQP 0-9-1 core specification explains the conditions under which ordering is guaranteed: messages published in one channel, passing through one exchange and one queue and one outgoing channel will be received in the same order that they were sent. RabbitMQ offers stronger guarantees since release 2.7.0.
Messages can be returned to the queue using AMQP methods that feature a requeue parameter (basic.recover, basic.reject and basic.nack), or due to a channel closing while holding unacknowledged messages. Any of these scenarios caused messages to be requeued at the back of the queue for RabbitMQ releases earlier than 2.7.0. From RabbitMQ release 2.7.0, messages are always held in the queue in publication order, even in the presence of requeueing or channel closure.
With release 2.7.0 and later it is still possible for individual consumers to observe messages out of order if the queue has multiple subscribers. This is due to the actions of other subscribers who may requeue messages. From the perspective of the queue the messages are always held in the publication order.
An exclusive queue is one which is deleted whenever the connection that declares it is closed. Although AMQP 0-9-1 allows you to declare a durable exclusive queue, the durability is meaningless since the queue will vanish anyway as soon as the broker stops. Therefore RabbitMQ will ignore the durable flag in a declaration of an exclusive queue and create an exclusive transient queue.
Similarly, there is no benefit to mirroring an exclusive queue, since such a queue will be deleted when the node it was declared on shuts down. Therefore RabbitMQ will never mirror exclusive queues.