Friday, December 17, 2004

Sequence Guard

I had the need recently to implement an orchestration that would process messages one at a time in a strict sequence only. It was wholly feasible that messages could be received out of order - therefore the orchestration would have to ensure that those messages would wait their turn to be processed until the missing message in the sequence arrived.

There are a couple of approaches I have seen for implementing a pattern such as this. One, for example, is the resequencer (from Enterprise Integration Patterns). I have a concern with its approach, namely that one orchestration instance is responsible for consuming all the messages and spitting them out in order - something which makes me feel uncomfortable, mostly I think due to the "single point of failure" placed on that one orchestration instance.

The Sequence Guard uses a different approach. One orchestration instance is associated with each message received. As its name implies, it uses a "Guard"; an additional receive shape that is correlated on the same sequence number as the first receive shape. It acts as a block on the orchestration. It will not let the orchestration progress until a matching guard for the message has been received.

When a matching guard is received, the orchestration can progress. At this point any processing logic you want can be carried out on your message, safe in the knowledge that it will only happen in the correct order, and one at a time.

The very last thing that the orchestration does is to send a new guard message that is next in the sequence. If that orchestration instance has already received the message then the arrival of the guard will make it process immediately otherwise it just waits for the message to arrive.

The Sequence Guard on first use must always be initiated by sending a guard message - after that it takes care of itself. Any messages received out of sequence will simply wait for the matching guard to arrive.

I tried to bind the receive and send ports for the guard message directly to the message box - but then correlation would not work. Therefore, you must ensure that the receive port for the guard message is receiving messages from the same place that the send port is sending guard messages to (a loopback). I do this by having the send port write out to a folder on the file system and the receive port read from that folder. A queue would probably be better.

Its interesting to watch this work through the HAT or through the debug viewer utility when you have a bunch of messages queued up all waiting for the missing message in the sequence. As soon as it arrives it causes a domino effect with all the waiting messages being activated and processed in turn.

Download the example here. There are some example message files provided with the download.

SequenceGuard