Message Ordering
Why this matters: ordering is the requirement that quietly destroys throughput, and the reason so many production queues advertise "we do not guarantee order." Knowing why it is expensive is what lets you decide when to pay for it.
Key takeaway
Some workloads require strict message ordering — a chat application must deliver messages in sequence to preserve context. Others tolerate messages arriving out of order, like email. Strict ordering is required for some use cases but unnecessary for others, and the design should not assume.
Best-effort ordering
In best-effort ordering, the system places messages in the queue in the order they arrive, regardless of their production sequence.
The worked example: a producer sends A, B, C, D. Network congestion delays B, so the server receives them as A, C, D, B. With best-effort ordering, the queue stores them in the order of their arrival rather than correcting the order.
Strict ordering
Strict ordering ensures that messages are queued in the exact sequence in which they were produced.
To achieve this, the system must identify the production order, typically using a unique identifier or timestamp assigned by the producer.
Three approaches to ordering
| Approach | How it works | Drawbacks |
|---|---|---|
| 1. Monotonically increasing numbers | The server assigns sequential numbers (1, 2, 3...) to messages as they arrive | Creates a bottleneck during traffic bursts. Does not account for network delays — a message produced earlier but arriving late gets a higher sequence number, violating the true production order |
| 2. Causality-based sorting | Messages are sorted by timestamps generated on the client side | Respects production time, but it is difficult to determine the correct order across multiple client sessions due to clock skew — differences in wall-clock time between machines |
| 3. Timestamps from synchronized clocks | Assigns unique, sequential timestamps that reflect the global order. A unique process identifier can be tagged to the timestamp to handle concurrent requests. Allows the server to identify and wait for delayed messages | Requires clock synchronization infrastructure |
Among these options, synchronized clocks provide the most robust mechanism for generating unique, ordered IDs.
As discussed in the sequencer building block, these numbers serve as both sequence IDs and globally synchronized wall-clock timestamps.
Sorting
To enforce strict ordering, the server must sort messages by their timestamps before processing. The system uses an online sorting algorithm to maintain order as new messages arrive — sorting incrementally rather than in batches, since the stream never ends.
When an old message arrives late
"Suppose a message sent earlier arrives late due to a network delay. What is the proper approach?"
The simple solution is to reorder the queue. Two scenarios can arise. First, reordering puts the messages in the correct order. Second, we have already handed out newer messages to the consumers.
If an older message comes in after we have already handed out a newer one, we put it in a special queue, and the client handles that situation. The client may later decide whether to consume the message if it does not affect the intended operation, or discard it if it is not needed.
Key takeaway
Only the producer knows production order, so it must stamp the sequence. Server-side counters order arrivals and confidently record the wrong answer. Synchronized clocks plus a process ID are the robust option — the sequencer building block, reused. And once a newer message has shipped, a late arrival can only be handed to the client.
Interview signal by level
| Level | What a strong answer sounds like |
|---|---|
| L4 | "The queue is FIFO, so messages come out in order." |
| L5 | Separates arrival from production: "arrival order isn't production order — the network reorders. If we need strict ordering the producer has to stamp a sequence number and the server sorts on it." |
| Staff+ | Ranks the mechanisms and names the limit: "a server-side counter is worse than nothing, because it records arrival order as if it were production order. Client timestamps hit clock skew across sessions. Synchronized clocks plus a process ID is the robust option — that's the sequencer building block. And I'd scope the guarantee like Kafka does: strict within a partition, nothing across partitions, because global order is far more expensive than it's usually worth." |
Next: what strictness costs, and how to manage concurrent access.