InterviewKata
System design · Worked design D34 of 46 · Data and infrastructure · Long form

Design a distributed message queue

Build the thing most other designs on this site lean on: a durable, partitioned log that thousands of producers write to and many independent consumers read from, at millions of messages a second. Think Apache Kafka, Amazon Kinesis or Pulsar. The core idea is one sentence; the interview is about what happens when brokers fail, consumers are slow, and one key gets all the traffic.

1. The prompt

"Design a distributed message queue that services use to publish events and consume them, durably and at high throughput."

What makes it hard:

2. Frame the problem

A work queue or an event log?

An event log: several independent consumer groups each read every message, and can re-read history.

That rules out deleting a message when it's consumed. Messages are kept for a retention period, and each group tracks its own position.

How much traffic?

About 1 million messages a second at peak, average 1 KB, so 1 GB/s in; consumers read it about three times over.

Far beyond one machine: the log must be split into partitions spread over many brokers.

How long do we keep data?

7 days by default; some topics forever (compacted).

1 GB/s × 7 days ≈ 600 TB before replication. Storage, not CPU, sizes the cluster.

Ordering?

Per key: all events for one order or one user, in order. No global order.

Messages with the same key go to the same partition; order holds within a partition.

Can we lose a message?

Not once it's acknowledged, for most topics. A few (metrics) can trade safety for speed.

Replication with a configurable acknowledgement level per producer.

Delivery guarantee to consumers?

At least once by default; exactly once for pipelines that read, transform and write back.

Consumers commit their position after processing; exactly once needs producer idempotence and transactions.

Latency?

Publish to consume in tens of milliseconds at p99.

Batching helps throughput but adds delay; we bound it by time.

3. What it must do

  1. A producer can publish a message (key, value) to a topic and learn when it's durably stored.
  2. A consumer group can read every message of a topic in order per key, sharing the work across its members.
  3. Messages are kept for the retention period, so a group can replay from any position.

Not now: per-message routing rules and priorities (a work queue's job), delayed delivery, schema registry, cross-region replication (a follow-up).

4. How well it must do it

TargetWhy this number
Throughput: 1 GB/s in, 3 GB/s out, growing 2× a year load shapeIt's the backbone for every other team's events.
Durability: an acknowledged message survives the loss of any one broker (and, with a stricter setting, two) data safety"Published" must mean published.
Latency: publish-to-consume under 50 ms at p99 speedDownstream systems react to events, not hourly batches.
Order per key, and no message skipped by a consumer group consistencyEvents for one order applied out of order corrupt state.
Availability: partitions keep accepting writes when a broker dies, within seconds surviving failuresProducers can buffer briefly, not for minutes.

5. Numbers that change the design

QuantityWorking→ so we…
Write throughput1 GB/s × replication factor 3 = 3 GB/s of disk writes; a broker sustains maybe 200–500 MB/s→ so we need tens of brokers and hundreds of partitions, sized by disk and network
Storage1 GB/s × 86,400 s × 7 days × 3 replicas ≈ 1.8 PBLocal disks get expensive → so old segments move to object storage (tiered storage)
Message size1 KB; a network round trip per message would cap a producer at a few thousand a second→ so producers batch (by size or a few ms) and compress batches
Partitions per topicA partition is consumed by one member of a group; a consumer does ~10–50 MB/sA 500 MB/s topic needs ≥ 20 consumers → so it needs ≥ 20 partitions, and more for growth

Illustrative numbers. Skipped: the number of topics (metadata only) and producer count (connections are cheap).

6. The nouns

partition      topic, id, leader_broker, replicas[], in_sync_replicas[], leader_epoch
segment files  partition/00000000000004180000.log  + .index (offset → file position) + .timeindex
message        offset, timestamp, key, value, headers             (inside compressed batches)
offsets        (group, topic, partition) → committed_offset       (itself stored in an internal, compacted topic)

7. The contract

Binary protocol over long-lived TCP (not REST: per-message HTTP is too costly)
Produce   {topic, partition (or key), batch[{key, value, headers}], acks: 0 | 1 | all}
  ← {base_offset} | error (NOT_LEADER → refresh metadata and retry)
Fetch     {topic, partition, from_offset, max_bytes, max_wait_ms}    (long poll)
  ← batches
CommitOffset  {group, topic, partition, offset}
JoinGroup / Heartbeat / LeaveGroup                                    (group membership)
Metadata  {topics} ← partition → leader map

Producers send to the partition leader directly; the key's hash picks the partition. A producer id plus a sequence number per partition lets the broker drop duplicate retries (idempotent producer).

Data flow: producer batches by partition → leader appends to its log → followers fetch and append → leader acknowledges per acks → consumers fetch from their committed offset → process → commit the next offset.

8. First working version

Brokers that append to partition logs on disk, a controller that knows the leaders, producers that send to leaders, consumer groups that pull.

8.1 Publishing

An append is a sequential write to the end of the current segment file: the operating system's page cache absorbs it and the disk sees large sequential writes, which is why one broker can take hundreds of MB a second.

8.2 Consuming as a group

Partitions are divided among the group's members; each partition has exactly one reader in the group, so order per partition is kept. The consumer commits the next offset after processing: if it crashes before committing, the next owner re-reads from the last commit (at least once).

8.3 Keeping and deleting

Retention deletes whole old segment files, never single messages: cheap and fast. Consumers can rewind to any retained offset to replay.

Parked for latera broker dies (9.1) · who decides the leader (9.2) · consumers join and leave (9.3) · a hot key (9.4) · duplicates (9.5) · a petabyte of retention (9.6).

9. Make it hold up

9.1 Replication and acknowledgements durability · availability

ApproachWhy
✗ BadOne copy per partition, on a RAID diskA dead machine takes its partitions offline, maybe forever.
~ GoodLeader plus followers; acknowledge when the leader has written (acks=1)Fast, but a leader that dies before followers copy the last messages loses messages the producer was told were safe.
✓ GreatLeader-follower with an in-sync replica set (ISR): followers pull from the leader; a follower that falls behind leaves the ISR; with acks=all the leader acknowledges once every ISR member has the message, and min.insync.replicas=2 refuses writes if fewer than two copies would existNo acknowledged message is lost while one in-sync copy survives. Only in-sync replicas can become leader. Producers that can afford loss still choose acks=1 for speed.

Try the trade-off in the lab in section 13: crash the leader after message 7 with each setting.

9.2 Who decides who leads availability · consistency

A controller holds the cluster metadata: partitions, replicas, ISR, leaders. It must itself be fault tolerant and agree on one truth: Kafka moved this from ZooKeeper to a built-in Raft quorum of controllers (KRaft). When a broker dies, the controller picks a new leader from the ISR for each of its partitions and bumps the partition's leader epoch; brokers and clients reject requests carrying an old epoch, so a returning old leader can't accept writes (fencing, Module 21). Rule to state: an out-of-sync replica must never become leader unless you explicitly accept data loss ("unclean" election).

9.3 Consumer groups and rebalancing no skipped messages

ApproachWhy
✗ BadConsumers pick partitions themselvesTwo readers on one partition, or none.
~ GoodA group coordinator; on any join or leave, everyone stops, gives up partitions and gets a new assignmentCorrect, but every deploy of a 100-member group pauses all consumption ("stop the world").
✓ GreatIncremental (cooperative) rebalancing with sticky assignment: only partitions that must move are revoked; static membership so a restarting consumer reclaims its partitions without a rebalanceDeploys and scaling cause small, local pauses instead of group-wide ones.

9.4 A hot key throughput · latency

All messages with one key go to one partition, so a celebrity user or a huge customer can overload one partition while others idle. Options: spread that key over several partitions with a suffix (losing per-key order unless you re-merge), give big tenants their own topics, or rely on the producer's partitioner only for keys that truly need order and use round-robin for the rest. Watch per-partition bytes in, not just the topic total.

9.5 Duplicates and exactly once consistency

ApproachWhy
✗ BadCommit offsets before processingA crash after the commit skips messages: at most once.
~ GoodCommit after processing; make consumers idempotent (dedupe by a message id, upserts)At least once with harmless duplicates: the right default.
✓ GreatIdempotent producers plus transactions for read-process-write pipelines: the output messages and the input offsets are committed atomically; readers skip uncommitted dataExactly once within the log. Effects outside it (an email, a payment call) still need idempotency keys.

9.6 A petabyte of retention cost

Keep recent segments on brokers' local disks for fast reads, and move older ones to object storage (D35); brokers fetch them back when a consumer replays history. Brokers become lighter, so adding or replacing one moves little data. For "latest value per key" topics (account settings, a table's changelog), use log compaction: old values for a key are removed in the background, keeping only the latest.

9.7 The whole design after the deep dives

10. Interviewer follow-ups

A consumer group is hours behind.

Lag (log end minus committed offset) is the alert. Scale out consumers up to the partition count; beyond that, more partitions are needed, which changes key placement, so plan partition counts ahead. Check whether processing, not fetching, is the bottleneck.

We need more partitions for an existing topic.

Adding partitions is easy but changes which partition a key hashes to, breaking per-key order across the change. Either over-provision partitions up front, or create a new topic and migrate consumers deliberately.

Replicate to another region.

Asynchronous mirroring of topics to a second cluster, with offset translation for consumers that fail over. Accept a small loss window, or run synchronous replication across nearby zones only.

One message is malformed and the consumer crashes on it forever.

After a few attempts, publish it to a dead-letter topic with the error and commit past it, so one poison message doesn't stop the partition.

Why do consumers pull instead of the broker pushing?

Each consumer reads at its own pace, batches naturally, and can rewind. Push needs per-consumer buffers and backpressure in the broker.

Why is it fast with ordinary disks?

Sequential appends, the page cache, batching and compression, and sending file data straight to the socket without copying through the application (zero copy).

Delete one user's data (privacy request).

Logs are append-only; options are short retention, compaction with a tombstone for the user's key, or encrypting each user's data with their own key and deleting the key.

Ten times the traffic.

More brokers and partitions, rebalanced gradually; tiered storage so new brokers don't copy history; quotas per client so one team can't starve others.

11. When things break

FailureWhat users seeHow we detect itWhat limits the damage
Broker diesSeconds of retries for its partitionsController heartbeatsNew leaders from the ISR; idempotent retries
Follower falls behindNothing; then writes refused if too few in syncISR shrink metricsmin.insync.replicas protects acknowledged data
Controller quorum loses a nodeNothingQuorum healthRaft majority keeps metadata available
Consumer crashDelay, some duplicatesGroup heartbeatsRebalance; resume from last commit
Disk fullWrites fail on that brokerDisk usage alertsRetention by size, tiered storage, quotas
Hot partitionHigh latency for one key's eventsPer-partition throughputKey splitting, dedicated topics

12. Wrap-up and the clock

Trade-offs we made:

What we'd watch: bytes in and out per broker and partition · under-replicated partitions · ISR shrink/expand rate · produce and fetch latency p99 · consumer lag per group · rebalance count and duration.

At 10×: more brokers and partitions, client quotas, tiered storage everywhere; the protocol and design stay the same.

A 60-minute interview

0–5Frame: log or queue, throughput, retention, ordering
5–10Must do, how well, numbers
10–15Nouns, protocol, data flow
15–25First version: produce, consume, retain
25–50Deep dives: replication (9.1), leadership (9.2), groups (9.3)
50–60Exactly once, follow-ups, wrap-up

A 90-minute interview

0–8Frame, including delivery guarantees
8–15Must do, how well, numbers
15–22Nouns, protocol, data flow
22–35First working version
35–75Deep dives 9.1–9.6 and the final picture
75–85Follow-ups: lag, repartitioning, other regions
85–90Trade-offs, metrics, 10×
LevelWhat a strong answer shows
Mid-levelTopics and partitions, an append-only log, consumer groups with offsets, replication to followers.
SeniorISR and acks with min.insync.replicas, leader election with epochs, rebalancing, at-least-once with idempotent consumers, hot keys.
Staff+Why sequential I/O and batching make it fast, exactly-once and its limits, tiered storage economics, partition planning for growth, multi-tenant quotas, cross-region trade-offs.

Common mistakes:

13. Try it, check yourself, read more

Acknowledgement lab. A producer sends messages 1 to 10 to a partition with one leader and two followers. Followers copy with a delay of two messages. The leader crashes right after message 7. Which acknowledged messages survive the failover?

Model: the follower that becomes leader is the most up to date one; messages it doesn't have are gone. Outlined = acknowledged to the producer. Illustrative.

How does a partitioned log keep order for one customer's events?

Order per key, not global order: that's what scales.

A leader dies and acknowledged messages are lost. What was the likely setting?

acks=all waits for every in-sync replica.

When should a consumer commit its offset for at-least-once delivery?

A crash before the commit means re-reading, not skipping.

What caps how many consumers in one group can work in parallel on a topic?

Each partition has exactly one reader in a group.
ResourceWhy
Kafka documentation: designPersistence, replication, ISR and delivery semantics, from the source.
KIP-500Why Kafka replaced ZooKeeper with its own Raft-based controller quorum.
KIP-405: tiered storageMoving old segments to object storage.
Exactly-once semantics in Kafka (Confluent)Idempotent producers and transactions, explained by their designers.