Overview of Service Bus transaction processing

This article discusses the transaction capabilities of Microsoft Azure Service Bus. Much of the discussion is illustrated by the AMQP Transactions with Service Bus sample. This article is limited to an overview of transaction processing and the send via feature in Service Bus, while the Atomic Transactions sample is broader and more complex in scope.

Transactions in Service Bus

A transaction groups two or more operations together into an execution scope. By nature, such a transaction must ensure that all operations belonging to a given group of operations either succeed or fail jointly. In this respect transactions act as one unit, which is often referred to as atomicity.

Service Bus is a transactional message broker and ensures transactional integrity for all internal operations against its message stores. All transfers of messages inside of Service Bus, such as moving messages to a dead-letter queue or automatic forwarding of messages between entities, are transactional. As such, if Service Bus accepts a message, it has already been stored and labeled with a sequence number. From then on, any message transfers within Service Bus are coordinated operations across entities, and will neither lead to loss (source succeeds and target fails) or to duplication (source fails and target succeeds) of the message.

Service Bus supports grouping operations against a single messaging entity (queue, topic, subscription) within the scope of a transaction. For example, you can send several messages to one queue from within a transaction scope, and the messages will only be committed to the queue's log when the transaction successfully completes.

Operations within a transaction scope

The operations that can be performed within a transaction scope are as follows:

Receive operations are not included, because it is assumed that the application acquires messages using the ReceiveMode.PeekLock mode, inside some receive loop or with an OnMessage callback, and only then opens a transaction scope for processing the message.

The disposition of the message (complete, abandon, dead-letter, defer) then occurs within the scope of, and dependent on, the overall outcome of the transaction.

Transfers and "send via"

To enable transactional handover of data from a queue to a processor, and then to another queue, Service Bus supports transfers. In a transfer operation, a sender first sends a message to a transfer queue, and the transfer queue immediately moves the message to the intended destination queue using the same robust transfer implementation that the autoforward capability relies on. The message is never committed to the transfer queue's log in a way that it becomes visible for the transfer queue's consumers.

The power of this transactional capability becomes apparent when the transfer queue itself is the source of the sender's input messages. In other words, Service Bus can transfer the message to the destination queue "via" the transfer queue, while performing a complete (or defer, or dead-letter) operation on the input message, all in one atomic operation.

See it in code

To set up such transfers, you create a message sender that targets the destination queue via the transfer queue. You also have a receiver that pulls messages from that same queue. For example:

var connection = new ServiceBusConnection(connectionString);

var sender = new MessageSender(connection, QueueName);
var receiver = new MessageReceiver(connection, QueueName);

A simple transaction then uses these elements, as in the following example. To refer the full example, refer the source code on GitHub:

var receivedMessage = await receiver.ReceiveAsync();

using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        // do some processing
        if (receivedMessage != null)
            await receiver.CompleteAsync(receivedMessage.SystemProperties.LockToken);

        var myMsgBody = new MyMessage
            Name = "Some name",
            Address = "Some street address",
            ZipCode = "Some zip code"

        // send message
        var message = myMsgBody.AsMessage();
        await sender.SendAsync(message).ConfigureAwait(false);
        Console.WriteLine("Message has been sent");

        // complete the transaction
    catch (Exception ex)
        // This rolls back send and complete in case an exception happens


A transaction times out after 2 minutes. The transaction timer starts when the first operation in the transaction starts.

Next steps

See the following articles for more information about Service Bus queues: