AMQP Transactions
Transactional messaging allows for the coordinated outcome of otherwise independent transfers, extending to an arbitrary number of message transfers spread across any number of distinct links in either direction.
An AMQP transaction scenario extends the roles of the two AMQP containers enclosing the Sender and Receiver nodes, to additionally act as a transactional controller and transactional resource respectively. This is established over a control link initiated by the controller, while allocation and completion are communicated using the AMQP declare and discharge type messages.
In an brokered environment such as one that involves Universal Messaging, AMQP transactions are involved in transactional publishing or transactional subscribing where the UM realm is the transactional resource and the AMQP application is the transactional controller.
Transactional Capabilities
In order to initiate an AMQP transaction, the container acting as the transactional controller establishes a control link to the container acting as the transactional resource. During this handshake, the containers exchange capability maps in order to determine if the desired transactional capability can be supported. The following table illustrates these capabilities:
AMQP Transactional Capability | AMQP Symbol | Description | UM 9.9 | UM 9.10 | UM 9.12+ |
Local Transactions | amqp:local-transactions | Indicates support local transactions. | | | |
Description: When a UM realm acts as an AMQP container, it advertises AMQP local transaction capability.
AMQP Transactional Capability | AMQP Symbol | Description | UM 9.9 | UM 9.10 | UM 9.12+ |
Distributed Transactions | amqp:distributed-transactions | Indicates support AMQP Distributed Transactions | | | |
Description: Distributed transactions are not currently defined by the AMQP specification.
AMQP Transactional Capability | AMQP Symbol | Description | UM 9.9 | UM 9.10 | UM 9.12+ |
Promotable Transactions | amqp:promotable-transactions | Indicates support AMQP Promotable Transactions | | | |
Description: Promotable transactions are not currently supported in Universal Messaging.
AMQP Transactional Capability | AMQP Symbol | Description | UM 9.9 | UM 9.10 | UM 9.12+ |
Multi Transactions Per Session | amqp:multi-txns-per-ssn | Indicates support multiple active transactions on a single session | | | |
Description: Multiple transactions per session are not currently supported in Universal Messaging. We plan to add support in a future product release.
AMQP Transactional Capability | AMQP Symbol | Description | UM 9.9 | UM 9.10 | UM 9.12+ |
Multi Sessions Per Transaction | amqp:multi-ssns-per-txn | Indicates support transactions whose txn-id is used across sessions on one connection | | | |
Description: Multiple sessions per transaction are not currently supported in Universal Messaging.
Transaction Declare
To begin transactional work, a transaction needs to be declared. After the control link is established, the transaction controller container sends a special AMQP message to the transaction coordinator container, whose body consists of the declare type in a single amqp-value section. A successful transaction declare, concludes with the coordinator container settling the declaration with a disposition outcome of declared, allowing the controller container to use it for message transfers.
Transaction Discharge
To conclude transactional work, a transaction needs to be discharged. This is done by the transactional controller container sending a special AMQP message to the transaction coordinator container, whose body consists of the discharge type in a single amqp-value section. In order to indicate a successful or failing discharge, the transaction controller container uses the fail flag on indicates that it wishes to commit or rollback the transactional work by setting the fail flag on the discharge body. Please note that the transaction coordinator can always also indicate a failing discharge if it is unable to honor the transaction controller's request.
Transactional Delivery State
AMQP transactions introduce an additional message delivery state called transactional state, which effectively wraps a regular message delivery state with a transaction ID. As discussed in previous sections, AMQP message deliveries consist of a notion of settlement which indicates when a node gives up trying to deliver a transfer and forgets all state. AMQP transactions do not change this model but interact with it as defined in the following sections.
NOTE: In Universal Messaging, AMQP delivery tags are uniquely identified using a long:long naming scheme consisting of <store unique ID>:<EID> . In transactional scenarios, the UM native TX ID is mapped to the AMQP TX ID.
Transactional Publishing
When the AMQP transaction controller container wishes to publish transactionally, it sets the delivery state to transactional-state, as explained in the previous section and pointing to a declared TX ID. This transactional state has the effect of the message not being available at the destination node of the transactional resource container, until after the transaction has been successfully discharged.
On receiving a non-settled delivery associated with a TX id, the transactional resource container sends a disposition response similar to the when non transactional publishing is used, with the delivery state being a transactional-state .
The following table illustrates how this is mapped to a successful Universal Messaging transactional publish, which is disposed with an accept state:
Remote Disposition State | UM Transformer Result | Sender Settle Mode | UM 9.9 | UM 9.10 | UM 9.12+ |
transactional | NOT null | N/A | | | |
Description: When UM receives a non-partial delivery transactional delivery, it tries to examine and transform its contents using the configured UM transformer. Following that, an asynchronous UM TX native publish request is queued. Finally, when the UM server has processed the native TX publish request, it will accept it with a transaction state set to the TX ID and advance the link ONLY If it is not already settled. If it is already settled, no further action will be taken. This is equivalent to a UM TX publisher.
The following table illustrates how this is mapped to an unsuccessful Universal Messaging transactional publish, which is disposed with a rejected state:
Remote Disposition State | UM Transformer Result | Sender Settle Mode | AMQP Errors | UM 9.9 | UM 9.10 | UM 9.12+ |
transactional | NOT null | N/A | amqp:unauthorized-access (fatal) amqp:resource-limit-exceeded (transient) amqp:internal-error (fatal) amqp:not-found (fatal) | | | |
As discussed in the accepted state mappings, a transactional AMQP publisher with these settings is mapped to an asynchronous UM TX native publish request getting queued. When the UM server has completed processing the native TX publish request, and an error has occurred, the delivery is rejected and settled as follows:
1. amqp:unauthorized-access : UM ACLs do not allow publishing for this subject. This is a fatal error so the link is closed.
2. amqp:resource-limit-exceeded: UM store capacity has been reached. This is a transient error so the link is advanced.
3. amqp:internal-error: An unexpected UM internal error has occurred. This is a fatal error so the link is closed.
4. amqp:not-found: The UM server is unable to find the destination node. This is fatal error so the link is closed.
Transactional Subscribing
When an AMQP transaction controller container wishes to associate the outcome of a delivery with a transaction it sets the state of the delivery to a transactional-state as explained in previous sections. and pointing to a declared TX id. This transactional state has the effect of the message not being released from the transactional resource container, until after the transaction has been successfully discharged.
On receiving a non-settled delivery associated with a TX id, the transactional controller container sends a disposition response similar to the when non transactional subscribing is used, with the delivery state being a transactional-state.
The following table illustrates how this is mapped to an successful Universal Messaging durable topic ack / transactional queue reader commit, which is disposed with an accept state:
Remote Disposition State | Delivery Settled | Receiver Settle Mode | Source Durability | UM 9.9 | UM 9.12 | UM 9.12+ |
transactional | N/A | N/A | N/A | | | |
Descruption: When UM receives an unsettled disposition frame with a remote state of transactional, it first extracts the delivery tag which is expected in the form <um store uniqueid>:<um event EID>, followed by extracting the durable name from the link. Following that it updates internal data structures in the sender session context resulting in a native durable subscriber ACK or UM native transactional queue reader commit getting queued. When the UM server has completed processing the request, the delivery is accepted and settled.
The following table illustrates how this is mapped to an unsuccessful Universal Messaging durable topic ack / transactional queue reader commit, which is disposed with a rejected state:
Remote Disposition State | Delivery Settled | Receiver Settle Mode | AMQP Errors | UM 9.9 | UM 9.10 | UM 9.12+ |
transactional | N/A | N/A | amqp:internal-error (fatal) | | | |
Description: As discussed in the accepted state mappings, a transactional AMQP consumer with these settings is mapped to a topic durable consumer or a queue transactional reader commit getting queued. When the UM server has completed processing the request, and an error has occurred, the delivery is rejected and settled as follows:
amqp:internal-error: An unexpected UM internal error has occurred. This is a fatal error so the link is closed.