Ordering, acks and delivery guarantees

Lesson 4.3 · 3 min read · by Vinay C

We'll cover the following

Any system that moves messages (chat, notifications, event streams) has to answer four questions: in what order, what counts as saved, what counts as delivered, and what happens on a retry.

Why clocks do not give you an order

  • Device clocks can be wrong or set by the user. Server clocks drift by milliseconds between machines.
  • Two messages can arrive within the same millisecond on different servers, so the timestamps tie or invert.
  • A timestamp cannot tell you that a message is missing.

A sequence number from one owner

  • Pick the unit that needs a shared order (one chat, one account, one partition). Hash its id to one owning shard.
  • That shard is the only thing that assigns a growing seq (1, 2, 3...) for the unit. No clock is involved.
  • The receiver spots a gap (saw 3 then 5) and asks for 4. A gap is detectable, which a timestamp never is.
  • Load is spread across thousands of units. Each unit sends few messages per second, so a counter per unit is trivial.
  • Kafka gives the same thing: a partition keyed by the unit id is an ordered log with offsets.

When does the sender see "saved"

  • The owner replicates the message to followers and acks only after a majority has it (2 of 3).
  • A crash right after the ack cannot lose it: any new leader needs a majority, and every majority contains a copy.
  • Acking after only the leader write risks a tick for a message that exists nowhere.
  • Why not wait for all replicas? One dead or slow replica would block every write. A majority tolerates one failure.
  • This is CP for that one unit: during a leader election that unit pauses for a moment, while everything else stays available.

Retries and duplicates

  • The network can drop the ack, so the client retries. The client generates an idempotency key (client_msg_id) per message.
  • The owner checks the key before assigning a seq. On a retry it returns the same seq and the same ack as the first attempt, not an error.
  • At-least-once delivery plus dedupe on the key gives effectively-once behaviour.

"Delivered" means the receiver acked

  • Store first, ack the sender second, deliver third. Never skip the store because the receiver looks online: a socket can look alive while the phone is already off the network.
  • Handing a message to a socket is not delivery. Delivery is the receiver device sending back an ack.
  • Delete the stored copy only after that ack (or when its retention TTL expires).
  • On reconnect the receiver sends a cursor per unit ("last seq I have is 41") and the server replays 42 onward. Each device keeps its own cursor.
Check yourselfB socket looks online but the phone has lost network. A already saw one tick. What protects the message?

It was stored and replicated to a majority before the tick, and it is only deleted after B device acks. When B reconnects it sends its cursor and the server replays what it missed.

Check yourselfWhy not order chat messages by server arrival timestamp?

Server clocks drift, timestamps can tie within a millisecond, and a timestamp cannot reveal a missing message. Use a per-conversation sequence number from a single owner instead.

Summary: say it in the interview

  • "One owner per conversation, a monotonic sequence number, majority ack. Store before deliver, delete on the device ack, replay from a cursor."