Delivered Ack
The WebSocket delivery ack — how the server knows Bob received the message
Bob's client doesn't ack every message individually. It sends one cumulative ack: "delivered up to seq N."
The question the design leaves open¶
We said: Bob's client receives messages over WebSocket and sends a delivery ack back. But what exactly does that ack look like?
Two options: 1. Per-message ack — one ack per message received 2. Cumulative ack — one ack covering all messages up to a seq number
Why per-message ack is wasteful¶
Bob comes back online after 3 days. Alice sent 50 messages. The WS server pushes all 50 over WebSocket.
With per-message acks:
Bob's client → server: "delivered seq=42"
Bob's client → server: "delivered seq=43"
Bob's client → server: "delivered seq=44"
... (50 times)
50 ack messages. 50 server-side message_status writes. For a batch delivery of 50 messages this is completely wasteful — every ack says the same thing except for the sequence number.
Cumulative ack — one message, full coverage¶
Bob's client receives the full batch of 50 messages. Once rendered, it sends one ack:
Bob's client → server: {
type: "delivered",
conversation_id: "conv_abc123",
seq: 91 ← the highest seq received
}
Server processes this as: "Bob has received everything up to seq=91 in conv_abc123."
UPDATE message_status
SET last_delivered_seq = 91
WHERE user_id=bob AND conversation_id=conv_abc123
One ack. One write. Full coverage.
Why this works — the seq number is monotonically increasing¶
Sequence numbers only go up. If Bob has received seq=91, he has necessarily received seq=42 through seq=90 as well (they were all delivered in the same batch, in order). The highest seq in the batch is sufficient to represent delivery of the entire batch.
Messages pushed to Bob: seq 42, 43, 44, ... 91
Bob's client renders all of them
Bob's client acks: seq=91
Server knows: Bob has seq 42 through 91 ✓
What if messages arrive out of order over WebSocket¶
In rare cases — network hiccups, reconnects — messages might arrive slightly out of order on the client. Bob's client buffers messages and only sends the cumulative ack once it has a contiguous sequence up to N. It does not ack seq=91 if seq=89 is missing.
Bob's client receives: 42, 43, 45 (44 missing)
→ holds 45, does not ack past 43 yet
→ 44 arrives (retransmit or reorder resolves)
→ now has 42, 43, 44, 45 contiguous
→ acks seq=45
This mirrors TCP's cumulative acknowledgement model — the receiver only acks up to the last contiguous byte received.
The ack triggers the double tick on Alice's side¶
Once the server updates last_delivered_seq, it checks if Alice is online:
Server receives Bob's ack: last_delivered_seq=91 for conv_abc123
→ UPDATE message_status SET last_delivered_seq=91 WHERE user_id=bob, conversation_id=conv_abc123
→ GET ws:alice from Redis registry
→ Alice is online on ws_server_2
→ Push event to Alice's client:
{ type: "status_update", conversation_id: conv_abc123, last_delivered_seq: 91 }
→ Alice's client: show double tick ✓✓ for all messages up to seq=91
If Alice is offline, this event is held and sent when she reconnects — covered in the offline status sync file.
Interview framing
"Bob's client sends a cumulative delivery ack — one message with the highest seq received. Not one ack per message. The server updates last_delivered_seq in a single write and pushes the double tick event to Alice if she's online."