Don’t assume message ordering in Azure Service Bus
One of the great fallacies of asynchronous messaging is that your messages will be processed in the order that they were sent. This is not a guarantee that any queuing system can provide and Azure Service Bus is no exception.
At the heart of this is the fact that the network is not reliable. Message queues are there to store messages until they can be safely transferred to the recipient. Performance and resilience normally takes precedence so ordered messaging tends to be sacrificed in order to minimise complexity.
Azure Service Bus only provides a delivery guarantee by default. This is not the same thing as guaranteeing to deliver messages in the order that they were sent, i.e. providing a “first-in-first-out” guarantee. Although it is technically possible to provide this guarantee, the resulting implementation undermines some of the benefits of a message queue, i.e. decoupling, buffering and scalability.
Even if you do manage to provide first-in-first-out guarantees for message delivery this is very different from the order in which messages are actually processed. If your messages must to be processed in a specific order then you cannot reply on the service bus infrastructure to provide this guarantee.
Sessions are not enough of a guarantee
Azure Service Bus does let you provide some level of ordering through message sessions. These need to be enabled when a queue is first created and each message has to be tagged with a session identifier before it can be sent. A receiver will use a MessageSession object created using this session identifier to receive messages in the correct order.
This isn’t enough to guarantee the order in which events are processed. For example, when a consumer reads a message using the default PeekLock mode it takes an exclusive lock for a set period of time. If this lock times out then the message is returned to the queue. This means it could be processed in the wrong order or even more than once.
The handling semantics for messages in PeekLock mode also allow you to explicitly abandon messages so they return to the queue. There’s nothing to stop another thread from taking the next message in the queue before you have had the chance to abandon the previous message.
You could try using ReceiveAndDelete mode so a message is removed from the queue as soon as it is read. However, this has a number of serious drawbacks. You lose the transactional nature of message handling and are at much greater risk of data loss.
Even if you are prepared to accept this risk, you can only truly guarantee the order in which messages are processed in ReceiveAndDelete mode by having a single processing thread. After all, your processing logic may be subject to variations in performance so each operation takes a different amount of time to complete.
How to avoid ordering issues
Attempting to guarantee the order in which events are processed does rather undermine the point of messaging. After all, a service bus should support asynchronous integrations and encourage loose coupling between applications. This is not compatible with having to guarantee the order in which events are processed. This creates temporal coupling between components so the integration resembles a stream of web service calls rather than a message bus.
The bottom line is that it shouldn't be the responsibility of an asynchronous messaging platform like Azure Service Bus to provide ordering guarantees. Ultimately, you should design your message payloads so the ordering doesn’t really matter.
If this isn’t possible then applications will just have to keep track of a sequence number or timestamp. If the event is older than the last processed event then it can be discarded.