Case study: Chat (WhatsApp, Messenger)
Design a chat system: persistent connections, idempotent sends with client message IDs, per-conversation ordering and delivery to devices that come and go.
40 min9 steps
By the end you can
- Estimate message rate, storage and the number of connection gateways
- Use client-generated message IDs and dedup to make retries invisible to users
- Guarantee per-conversation order with partitioned storage and delivery
- Predict the effect of lost acknowledgements, a delivery-worker crash and a slow store
Requirements
Features
- Groups, history, receipts; online push and offline inboxes
Guarantees
- Per-conversation order; durable sent tick; no duplicate display; regional p99 <200 ms
Estimates
Messages
230k/s
500M users * 40 / 86,400; peak 500k/s
History
4 TB/day
20B * 200 B; 1.5 PB/year before replication
Gateways
200-400
200M concurrent devices; roughly 1M/host
Group fan-out
20 members
Delivery work exceeds send rate
API
Send
WS: conversationId, client messageId, body; reuse ID on retry
Accept
Durable append -> ack with serverSeq
Deliver
Push serverSeq; client acknowledges delivery
History
GET /v1/conversations/{id}/messages?before=seq
Data model
Messages
conversationId partition; serverSeq clustering
Inbox
Pending per-user deliveries; delete after acknowledgement
Dedup
sender + messageId; roughly one-day TTL
High-level design
Order the chat service's work for an incoming message.
- 1Publish the message to the delivery log, keyed by conversation
- 2Record (sender, messageId) in the dedup store; if it already exists, ack and stop
- 3Ack the sender with messageId and serverSeq
- 4Append the message to the conversation's partition with the next serverSeq
Deep dive: ordering and exactly-once for people
Retry
- Reuse client messageId; dedup at server and recipient
Order
- Assign serverSeq per conversation; fill gaps from history
Alternatives
Idempotent push
- Durable sends; duplicate-free display
No key
- Lost ack -> duplicate message
Polling
- 200M / 5 s = 40M mostly empty requests/s
Relational store
- Works at smaller scale; wide-column partition appends scale further
Failure scenarios you can run
Inject
80 ms response loss
Observe
Stored message retried with same ID
Recover
Dedup returns sent without another append
Inject
Workers down 0.5 s
Observe
Durable log buffers messages
Recover
Replay conversation partition in order
Inject
Store latency x8
Observe
Timeouts trigger retries
Recover
Budget retries to avoid overload
Wrap up
Protect
Jitter reconnects; cap gateway admission
Watch
Huge-group hot partitions; store retry load
Review
Gateways, durable IDs, ordered log, offline inbox
Then score each stage from 1 to 4.
Answer this step to continue.