Sending Transactional Messages to Multiple Destinations

Updated: June 25, 2007

Applies To: Windows Server 2008

Both distribution lists and multiple-element format names have support for transactional messaging. When a message is sent to a distribution list or a multiple-element format name within a transaction, a copy of the message is sent to each applicable outgoing queue. Failure in sending the message to any of these outgoing queues will abort the entire transaction. A distribution list or a multiple-element format name may reference a mixture of transactional and nontransactional target queues. When a transactional message is sent to such a mixture of targets, the message is allowed to be sent to all the transactional and nontransactional target queues on the local or a remote computer. However, when the message reaches a nontransactional queue, it is rejected and transferred to the transactional dead-letter queue on the computer hosting the nontransactional queue for storage, and a negative acknowledgement message indicating that a transactional message was sent to a nontransactional queue is sent to the administration queue specified in the original message.

Message Queuing guarantees that transactional messages will be delivery in-order and only once. The Exactly-Once In-Order Delivery (EOD) protocol is an end-to-end protocol, guaranteeing that messages are not duplicated or delivered out-of-order. To achieve this, the sender attaches information to each message that allows the receiver to eliminate duplicate and out-of-order messages. All messages that have order relation between them are said to be in the same message stream. Each message in a stream carries a sequence number, identifying the message in the stream, and a previous number identifying the preceding message in the same message stream. The first message in a stream always carries a previous number equal to zero.

Each receiver filters the newly arriving messages and accepts only those messages that arrive in-order and are not duplicates. To implement this, the receiver uses the sequence numbers and previous numbers attached to the messages. The receiver needs to keep track of the sequence number of the last message accepted in each message stream, there is no need to keep message history or a larger database. The receiver acknowledges by sending the sequence number of the last message accepted back to the sender. This lets the sender know that the receiver accepted all messages up to the acknowledged message.

The sender tags each message with a monotonically increasing sequence number and links it to the previous message using the previous number. When there is no previous message in the message stream the sender places a zero in the previous number field. This ensures that the receiver will accept the message. A message that is discarded at the sender side (for example, when the time to receive the message before time-out has elapsed) is removed from the message stream, and the next message is linked to the message preceding the removed message.

The sender needs to keep resending the same messages until an "order-acknowledgment" arrives from the receiver. The acknowledgment carries a sequence number indicating that all messages up to that number have safely arrived in-order and exactly-once at the target. The sender can thus safely discard all messages up-to and including the indicated sequence number.

To overcome crash scenarios where the receiver cannot recover its database (last accepted message sequence number for a given message stream), the sender must assign the first message in the queue a previous number of zero. On the other hand, after a sequence number has been attached to a message, it must never be changed.

The receiver keeps track of the last accepted message sequence number for each message stream. It uses this number to filter out arriving messages and accept only those that arrive in-order and are not duplicates. The receiver initiates this last number with a zero for each new message stream. The simplest algorithm the receiver must implement looks like the following:


if(message.sequence <= last_accepted_sequence)
{
    Reject Message
}

if(message.previous > last_accepted_sequence)
{
    Reject Message
}

Accept Message

last_accepted_sequence = message.sequence;

When a new message stream has been created, the receiver occasionally sends an "order-acknowledgment" containing the last accepted sequence number. This acknowledgment signals the sender that the target has accepted all messages in the message stream up to the indicated sequence number. The receiver keeps sending these acknowledgments for active message streams; that is, message streams for which messages continue to arrive even if these messages are not accepted.

The receiver sends these "order-acknowledgments" to the acknowledgment return address indicated by the incoming message. All messages within the same message stream must have the same acknowledgment return address.

It is recommended that the quality of service for acknowledgment messages be as lightweight as possible because the receiver maintains the state to regenerate the acknowledgment persistently (last sequence number).

For more information about sending messages within transactions, see Transactional Messaging.

For information about sending transactional messages to distribution lists, see Distribution Lists.

For information about sending transactional messages to multiple-element format names, see Multiple-Element Format Names.

Community Additions

ADD
Show: