In this article
Before you start
This page belongs to the distributed systems interview guide, where the round itself is explained.
Why interviewers ask this
A message queue sits between almost every pair of services in a large system, so a weak design here breaks everything around it. Candidates report prompts such as designing a Kafka-like distributed queue, an event-driven system for incoming logs or a stock feed, and a multi-channel queue with channel subscriptions.
A weak answer draws a box called "queue" between two services and stops. A strong answer says how messages are split across machines, how a consumer remembers its place, what happens when a consumer crashes mid-message, and what order is kept. A Staff-level answer sizes the system with arithmetic and names the failure that arithmetic reveals, such as one hot key overloading one partition. The same ideas appear in the ad click aggregator and chat system designs.
Message queue and pub/sub from first principles
- Consumer group
- A consumer group is a set of consumer processes that share the work of reading one topic, where in a classic group each partition is read by exactly one member at a time, so adding members adds parallelism up to the partition count.
- Offset
- An offset is the position of a message inside a partition, counted from the start, and a consumer group stores the offset of the next message it has not finished, so after a restart it resumes from that position.
- Acknowledgement
- An acknowledgement is a signal from a consumer to the broker that a message has been handled, after which the broker may advance the offset or delete the message; when the signal is sent decides what a crash costs.
In plain words
Think of a restaurant order rail. Waiters clip tickets on the rail. Cooks take tickets when free. Waiters never wait for cooks. That rail is a queue. The analogy breaks in one place: a real rail has one copy of each ticket, while a broker can keep the ticket and let several kitchens read it.
Pub/sub is a pattern in which publishers send messages to a named channel called a topic, and every subscription on that topic receives its own copy. A queue is the opposite case: each message goes to one consumer. One design covers both. Keep messages in a log, and let each group of consumers read it with its own offset. One group gives queue behaviour. Several groups give pub/sub.
First, clarify requirements and the interface
Before drawing boxes, a strong answer fixes what the system must do. The interface is small: publish(topic, key, payload) returns a partition and an offset, subscribe(group, topic) joins a consumer group, and commit(offset) records progress. Then ask for the targets: messages per second and bytes per message, how long messages are kept, whether an acknowledged message may ever be lost (durability), and whether order matters globally, per key or not at all.
A second component is easy to forget: metadata. Something must record which topics and partitions exist, which machine leads each partition and which consumer owns which partition. Kafka uses a controller for this, described in the Kafka documentation; in an interview, say it is a small replicated service reached by consensus, kept off the message path.
Step 1: A producer appends to one partition of a log
A producer sends a message with a key, such as a user identifier, and the broker appends it to the end of exactly one partition of the topic. The broker picks the partition by hashing the key and taking the remainder after dividing by the partition count. The same key lands in the same partition while the partition count stays fixed, which is how keyed order is kept per partition (Kafka documentation). Unlike consistent hashing for a distributed cache, a plain hash-mod-N rule moves most keys when the partition count changes, so adding partitions later breaks per-key order for the keys that move; choose the count with headroom up front.
Input: a key and a payload. Output: a partition and an offset. In this design, order holds only inside one partition, because two partitions are separate sequences on separate machines. Messages for one user stay in order; messages for different users do not. Appending to the end of a file is a cheap operation, which is why a log is a good storage shape. You now know where a message lives; next comes how it is read.
Step 2: Consumer groups read with their own offsets
Each consumer group keeps one offset per partition, and a consumer reads forward from that offset. In a classic consumer group, each partition is assigned to one member, so two members never process the same partition at the same time (Kafka documentation). If a topic has 20 partitions (illustrative), a classic group can use at most 20 busy members; a twenty-first sits idle.
Input: the group's stored offsets. Output: the next batch of messages. A second group on the same topic has separate offsets, so it receives every message again; that is pub/sub. Because the log is kept for a retention period, a new group can also start from the oldest message and replay history. Kafka consumers pull messages at their own pace rather than being pushed to, so a slow consumer does not push back on the producer (Kafka documentation). Next: what happens when a consumer fails.
Step 3: Acknowledge after processing, and expect duplicates
A consumer sends its acknowledgement (RabbitMQ consumer acknowledgements) either before or after it processes a message, and the order is the whole delivery-semantics question. Ack before processing: a crash in between loses the message, so delivery is at-most-once. Ack after processing: a crash in between means the broker never saw the ack and delivers the message again, so delivery is at-least-once.
In a partitioned log the acknowledgement is an offset commit: committing offset N acknowledges every message before it, so a crash replays everything since the last commit, not one message. Committing after every message gives the fewest duplicates but the most commit writes; committing once per batch cuts the writes and can replay a whole batch. Kafka keeps each group's committed offsets in an internal topic (Kafka documentation).
Most designs choose at-least-once. The Amazon Simple Queue Service (SQS) documentation states that standard queues ensure at-least-once delivery, that more than one copy of a message might be delivered, and that messages may occasionally arrive out of order. In SQS, a visibility timeout is a period during which a delivered message is withheld from other consumers; if no ack arrives before it ends, the message becomes available again.
An idempotent consumer is a consumer whose handler gives the same final result when it sees a message twice, for example by storing processed message identifiers. The payment system design uses the same idea for charges. A message that fails every retry moves to a dead-letter queue, a separate queue that holds poison messages for inspection.
Step 4: Replicas and retention keep messages safe
Each partition is copied to replicas on other machines, and one copy leads (Kafka replication). Whether an acknowledged write survives losing a machine depends on settings: in Kafka, a producer with acks=all gets its reply only after the in-sync replicas hold the message, and min.insync.replicas sets the least number of in-sync copies a write needs. If the leader fails, the controller elects a new leader from the in-sync replicas. The price is more write latency and more storage: each extra copy adds one more full copy of the data.
Input: an append to the leader. Output: the same message on the replicas, then a success reply. Retention is the second control. The broker keeps messages for a set time or size, then deletes the oldest (Kafka documentation), which bounds disk use and decides how far back a new consumer group can replay. You now have the whole system: a keyed append, groups with offsets, ack-after-processing with retries and a dead-letter queue, and replicas. The figure shows the four steps in order.
Step-by-step description
- The key picks one partition of the log.
- Each group reads with its own offset.
- Repeated failures go to a dead-letter queue.
- Partitions are copied to replicas.
- A producer appends each message to one partition chosen by hashing its key.
- Each consumer group keeps its own offset in the log.
- A message that fails repeatedly moves to a dead-letter queue.
- Each partition is copied to replicas.
A producer appends each message to one partition chosen by hashing its key. Each consumer group keeps its own offset in the log. A message that fails repeatedly moves to a dead-letter queue. Each partition is copied to replicas.
Knowing it is half the job. Saying it is the other half.
Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.
Start Free Trial →Worked example
Size a queue for an event feed with an illustrative peak of 50,000 messages per second, 1,000 bytes per message, 7 days of retention and 3 copies of each message.1 The incoming bandwidth is 50 MB per second, which is 4,320,000,000 messages and 4.32 TB per day.1 Seven days of one copy is 30.24 TB, and three copies is 90.72 TB.1
| Quantity | Value (illustrative workload) |
|---|---|
| Incoming bandwidth | 50 MB per second |
| Data per day, one copy | 4.32 TB |
| Seven days retained, one copy | 30.24 TB |
| Seven days retained, three copies | 90.72 TB |
| Minimum partitions at 5 MB per second each | 10 |
| Partitions with 2 times headroom | 20 |
| Consumers needed at 2,500 messages per second each | 20 |
| Even share of one partition among 20 | 5% |
| Share of the hottest partition with skewed keys | 17.26% |
| Hottest partition rate | 8,631 messages per second |
Partitions follow from bandwidth: at an assumed 5 MB per second per partition, 50 MB per second needs 10 partitions, and doubling gives 20 for headroom.1 Consumers follow from processing speed: at 2,500 messages per second each, 20 consumers match the 50,000 per second arriving, exactly one per partition.1
Now add skew. Take 1,000 user keys whose traffic is proportional to one over the key's popularity rank (illustrative) and hash them to 20 partitions. The hottest partition receives 17.26% of messages and the coolest 2.75%, against an even 5%.1 The hottest one then gets 8,631 messages per second, far above the 2,500 its single consumer handles, so it falls behind by 6,131 messages per second while others sit near idle.1
Text description of the diagram
- The even share of one partition among 20 is 5%.
- With skewed keys the hottest partition receives 17.26% of messages and the coolest 2.75%.
Two more computed results. With one commit per message (illustrative) and a 1% chance (illustrative) that a consumer crashes after processing but before acking, 1,000,000 simulated messages produced 10,028 duplicates; committing once per batch would replay more messages per crash.1 And after a 10-minute consumer outage the backlog is 30,000,000 messages; with 20 consumers there is no spare capacity to drain it, and 30 consumers drain it in 20 minutes, but only if the topic has at least 30 partitions.1 Partition count caps parallelism, so choose it with headroom: going from 20 to 30 partitions with a plain hash-mod-N rule remaps 66.7% of keys, and those keys lose their order across the change.1
Trade-offs and edge cases
The three common shapes trade replay and fan-out against simplicity. A work queue gives each message to one consumer and suits background jobs. A pub/sub topic gives every subscription its own copy and suits notifications. A partitioned log does both with groups, and also lets a new group replay old messages.
Text description of the diagram
- In a work queue each message goes to one consumer.
- In a pub/sub topic every subscription gets a copy.
- In a partitioned log every consumer group gets a copy and can replay from any offset.
In a work queue each message goes to one consumer. In a pub/sub topic every subscription gets a copy. In a partitioned log every consumer group gets a copy and can replay from any offset.
Edge cases decide a real design:
- Ordering with retries. A retried message arrives after later ones, so order is not safe unless the consumer handles one message per key at a time. The SQS page above says standard queues only make a best-effort attempt to keep order.
- Poison messages. One message that always fails blocks its partition if you retry forever; cap retries and use a dead-letter queue.
- Hot keys. As the worked example shows, hashing a skewed key overloads one partition; salt the key or split the hot key across partitions and lose its strict order.
- Changing the partition count. With hash-mod-N placement, adding partitions remaps most keys, so per-key order breaks for the keys that move (Kafka documentation); pick the count with headroom, or plan a migration to a new topic.
- Rebalancing. When a consumer joins or leaves, partitions move between members and reading pauses briefly.
- Backpressure. Watch consumer lag, the gap between the newest offset and the group's offset; rising lag means add consumers or partitions.
The 2025–26 state moved on one limit. As of October 2026, Apache Kafka 4.x adds share groups (Kafka Improvement Proposal (KIP) 932, Queues for Kafka), where several consumers read one partition and acknowledge messages one by one, so the one-member-per-partition limit applies to classic groups only.
Exactly-once is supported inside a broker's own loop but stops at its edge. As of October 2026, Kafka offers an idempotent producer and transactions that give exactly-once semantics for read-process-write pipelines between Kafka topics (Kafka message delivery semantics), and SQS first-in-first-out (FIFO) queues deduplicate messages within a 5-minute window (Amazon SQS FIFO exactly-once processing). Neither covers a side effect in an external system, such as a database write or a payment call; there the route is at-least-once delivery plus an idempotent consumer. Producer retries are a second source of duplicates: a producer that times out and resends may store the message twice unless the broker deduplicates it. More concurrency in the consumer raises the usual concurrency hazards.
How to answer it in an interview
How would you design a message queue that also supports pub/sub?
I would build a partitioned append-only log. Producers pick a partition by hashing a key, so messages with the same key stay in order. Each consumer group tracks its own offset, so one group gives queue behaviour and several groups give pub/sub. Consumers acknowledge after processing, so delivery is at-least-once and handlers must be idempotent. I would replicate each partition, add a dead-letter queue for poison messages, and watch consumer lag. The main trade-off is that order holds only within a partition, and a hot key can overload one consumer.
The spoken structure: say the log and partitions, then groups and offsets, then the ack order and its duplicates, then the trade-off. Size it with numbers if asked; the backend interview questions page shows neighbouring prompts.
Common mistakes
- "The queue keeps all messages in order." (wrong) Order holds only inside a partition. (right) "Order is kept per key, because a key maps to one partition."
- "We ack first so we never process twice." (wrong) A crash after the ack loses the message. (right) "I ack after processing and make the handler idempotent."
- "Exactly-once means my database writes happen once too." (wrong) Broker exactly-once covers its own read-process-write loop, not side effects in external systems. (right) "I use broker exactly-once inside the pipeline and an idempotent consumer for anything outside it."
- "More consumers always means more speed." (wrong) A classic consumer group cannot use more members than partitions. (right) "I set the partition count above the consumers I expect to need."
Follow-up questions
How do you handle stock ticker changes in an app: pub/sub or polling?
Use pub/sub when updates are frequent and many clients need them: the server pushes each change to subscribers, while polling wastes requests when nothing changed and adds delay when it did. Polling is simpler and fits rare updates; the webhook page covers the push-versus-poll trade.
What is the best way to show historical prices of a stock to the client?
Keep the live feed on the log, but write each tick into a store built for time ranges, such as a time-series database partitioned by time, and serve charts from that store. The log is for delivery and replay; it is not a query engine.
How do you design an event-driven system where logs keep arriving?
Make log producers append to a topic keyed by source, run one consumer group per downstream use such as search indexing, alerts and archiving, and size partitions from the arrival rate as in the worked example. Each group reads at its own pace.
What is the difference between a message queue and pub/sub?
A queue delivers each message to one consumer among competing consumers; pub/sub delivers each message to every subscription. A log with consumer groups provides both. For a multi-channel queue, treat each channel as a topic with its own partitions and subscriptions, and let each subscriber group keep an offset per channel.
Can a message queue deliver each message exactly once?
Inside one broker's read-process-write loop, yes: Kafka's idempotent producer and transactions support it, and SQS FIFO queues deduplicate within a 5-minute window (see the trade-offs above for sources). For side effects in an external system, a lost ack or producer retry can still cause a repeat, so combine at-least-once delivery with an idempotent consumer that skips messages it has already handled.
What if peak traffic doubles?
At an illustrative 100,000 messages per second the bandwidth is 100 MB per second, so the log needs 40 partitions with the same headroom and 40 consumers at 2,500 messages per second each.1 Adding partitions to a hash-mod-N topic remaps most keys and breaks per-key order for them, which is why the count is chosen with headroom at the start.
- A message queue decouples producers from consumers; pub/sub gives every subscription its own copy.
- A partitioned log with per-group offsets covers both queue and pub/sub behaviour.
- Order holds only within a partition, so choose the key carefully.
- Acknowledging after processing gives at-least-once delivery, so handlers must be idempotent.
- Partition count caps parallelism, and a hot key can overload one partition.
Sources
- Amazon SQS standard queues — Amazon Web Services (retrieved 2026-10-26), 26 Oct 2026
- Apache Kafka documentation: design — Apache Software Foundation (retrieved 2026-10-26), 26 Oct 2026
- Apache Kafka documentation — Apache Software Foundation (retrieved 2026-10-26), 26 Oct 2026
- Apache Kafka documentation: message delivery semantics — Apache Software Foundation (retrieved 2026-10-26), 26 Oct 2026
- Apache Kafka documentation: replication — Apache Software Foundation (retrieved 2026-10-26), 26 Oct 2026
- KIP-932: Queues for Kafka — Apache Software Foundation (retrieved 2026-10-26), 26 Oct 2026
- Amazon SQS FIFO queues: exactly-once processing — Amazon Web Services (retrieved 2026-10-26), 26 Oct 2026
- Amazon SQS visibility timeout — Amazon Web Services (retrieved 2026-10-26), 26 Oct 2026
- RabbitMQ consumer acknowledgements and publisher confirms — RabbitMQ (retrieved 2026-10-26), 26 Oct 2026