AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content
SD notes

System Design · Note 10 of 22

Message Queues

Intermediate · 7 min read · updated 29 October 2026

Quick answer

What is a message queue in system design?

A message queue is a buffer between services: producers send messages to it and consumers read them at their own pace. It decouples the two sides, absorbs traffic bursts, lets work be spread over many consumers and, with acknowledgements and retries, keeps a message from being lost when a consumer crashes.

On this page
  1. Why a queue
  2. Point-to-point: competing consumers
  3. Publish/subscribe
  4. Acknowledgements and redelivery
  5. Dead-letter queues
  6. Ordering and tools
  7. Interview questions
  8. Common questions

A queue sits between the service that has work and the service that does it, so neither has to wait for the other.

Why a queue

  • Decoupling: the producer knows the queue, not the consumer.
  • Buffering: bursts wait in the queue; consumers work at a steady rate.
  • Scaling out: add consumers to drain faster.
  • Reliability: a message survives a consumer crash until it is acknowledged.
  • Cost: a new component to run, and work happens later (asynchronously).
A queue absorbs a burst and the consumer drains it at a steady rate

The producer and the consumer only know the queue. The consumer works at a steady 2 messages per tick, and the queue is empty.

0:01 / 0:18Step 1 of 6
Transcript · 6 steps

Point-to-point: competing consumers

  • Many consumers read one queue.
  • Each message goes to exactly one consumer, then is hidden from the others.
  • Fast consumers take more work; one slow message does not stall the rest.
  • Use it for jobs: sending emails, resizing images, processing orders.
Point-to-point: competing consumers, each message processed by one

7 messages wait in one queue with three consumers reading it. Each message will go to exactly one of them: they compete for the work.

0:01 / 0:21Step 1 of 6
Transcript · 6 steps

Publish/subscribe

  • Producers publish to a topic; every subscriber gets its own copy.
  • The publisher does not know who listens; add a subscriber without changing it.
  • Use it for events: "order placed" feeds email, analytics and search.
Publish/subscribe: every subscriber gets a copy of every event

The order service publishes events to a topic. It does not know who listens; three services have subscribed.

0:01 / 0:17Step 1 of 6
Transcript · 6 steps

Acknowledgements and redelivery

  • A delivered message is hidden, not deleted, for a visibility timeout.
  • The consumer acks after the work; the ack deletes it.
  • No ack in time (crash, timeout) → the message is delivered again.
  • That is at-least-once delivery, so consumers must be idempotent.
Acks and redelivery: a crash before the ack means the message comes back

Message m1 says "charge order #42". The consumer will charge the card, then acknowledge, and only an ack deletes a message from the queue.

0:01 / 0:25Step 1 of 8
Transcript · 8 steps

Delivery guarantees

At-most-onceAt-least-onceExactly-once effect
HowAck before the workAck after the workAt-least-once + idempotent consumer
RiskLost messagesDuplicatesMore work to build
Typical useMetrics you can dropMost jobsPayments, orders

Dead-letter queues

  • Retries fix passing faults (a timeout, a busy database), ideally with backoff.
  • A poison message fails every time; retrying forever blocks the queue.
  • After N failed deliveries it moves to a dead-letter queue for inspection and replay.
  • Alert on DLQ depth: it means something is broken.
Dead-letter queue: a message that keeps failing is set aside after N tries

The consumer takes messages from the head of the queue. m7 has a malformed payload that makes the consumer fail every time; the queue allows 3 deliveries per message.

0:01 / 0:26Step 1 of 9
Transcript · 9 steps

Ordering and tools

  • A single queue can be FIFO, but competing consumers break strict order; order is usually kept per key (a partition or message group).
  • RabbitMQ, Amazon SQS: a message is deleted once acked.
  • Kafka: a retained, partitioned log; each consumer group tracks its own offset and can replay.

Interview questions

  • Why put a queue between two services? To decouple them, absorb bursts and let the consumer fail or scale without losing work.
  • Queue or pub/sub for "order placed"? Pub/sub: several services each need the event.
  • A consumer crashes mid-message. What happens? No ack arrives, so after the visibility timeout the message is redelivered.
  • Why must consumers be idempotent? At-least-once delivery can repeat a message; processing it twice must not repeat the effect.
  • What is a dead-letter queue for? Holding messages that keep failing, so they stop blocking the queue and can be fixed and replayed.

Next: Rate Limiting · Before this: Consistent Hashing

Common questions

Why use a message queue instead of a direct API call?

A direct call needs the receiver up and fast enough at that moment. With a queue the producer only needs the queue: the consumer can be slow, restarting or briefly down, and work waits instead of failing. It also lets you add consumers to drain work faster.

What is the difference between a queue and pub/sub?

In a point-to-point queue each message is processed by one of the competing consumers, which spreads the work. In publish/subscribe each subscriber gets its own copy of every message, so one event can update search, send an email and feed analytics.

What does at-least-once delivery mean?

The queue guarantees a message is not lost, but it may be delivered more than once: if a consumer crashes after doing the work but before acknowledging, the message is redelivered. Consumers must therefore be idempotent, so a second delivery has no extra effect.

How do you make a consumer idempotent?

Give each message a unique id and record processed ids in the same transaction as the work, skipping any id already recorded. Or make the operation naturally idempotent, such as setting a status instead of incrementing a counter, or using an idempotency key with the payment provider.

What is a dead-letter queue?

A separate queue where messages go after failing a set number of deliveries. It stops one bad message (a poison message) from being retried forever and blocking others, and keeps it with its error so someone can inspect it, fix the cause and replay it.

Is Kafka a message queue?

Kafka is a distributed log rather than a classic queue. Messages are kept for a retention period instead of being deleted when consumed, and each consumer group tracks its own offset, which gives pub/sub across groups and competing consumers within a group, one consumer per partition. RabbitMQ and SQS delete a message once it is acknowledged.

Message Queues in System Design, Animated

Message queues for system design interviews: buffering bursts, competing consumers vs pub/sub, acks, redelivery, idempotency and dead-letter queues, animated.

  • Subject: System Design
  • Level: Intermediate
  • Reading time: 7 min
  • Updated: 2026-10-29

What is a message queue in system design?

A message queue is a buffer between services: producers send messages to it and consumers read them at their own pace. It decouples the two sides, absorbs traffic bursts, lets work be spread over many consumers and, with acknowledgements and retries, keeps a message from being lost when a consumer crashes.

A queue sits between the service that has work and the service that does it, so neither has to wait for the other.

Why a queue

  • Decoupling: the producer knows the queue, not the consumer.
  • Buffering: bursts wait in the queue; consumers work at a steady rate.
  • Scaling out: add consumers to drain faster.
  • Reliability: a message survives a consumer crash until it is acknowledged.
  • Cost: a new component to run, and work happens later (asynchronously).
Producersent 0QueueConsumer2 per tick89003420t0t1t2t3t4t5queue depth after each tick
A queue absorbs a burst and the consumer drains it at a steady rate.
  1. The producer and the consumer only know the queue. The consumer works at a steady 2 messages per tick, and the queue is empty.
  2. 1 message arrives and the consumer takes it at once. While traffic is below the consumer's rate, the queue stays empty.
  3. A burst: 5 messages arrive in one tick but the consumer takes only 2. The queue absorbs the rest and holds 3, instead of the consumer being overwhelmed.
  4. 3 more arrive while 3 still wait, and the consumer still takes only 2. The backlog grows to 4; the producer is never told to slow down.
  5. No new messages. The consumer keeps draining at its own pace: 2 processed, 2 still waiting.
  6. The backlog is gone: all 9 messages processed, none lost, and the consumer never ran faster than 2 per tick. The queue turned a spike into a steady flow.

Point-to-point: competing consumers

  • Many consumers read one queue.
  • Each message goes to exactly one consumer, then is hidden from the others.
  • Fast consumers take more work; one slow message does not stall the rest.
  • Use it for jobs: sending emails, resizing images, processing orders.
QueueConsumer 1done: 2Consumer 2done: 2Consumer 3done: 364
Point-to-point: competing consumers, each message processed by one.
  1. 7 messages wait in one queue with three consumers reading it. Each message will go to exactly one of them: they compete for the work.
  2. Each free consumer takes the next message from the head: consumer 1 gets 1, consumer 2 gets 2, consumer 3 gets 3. A taken message is hidden from the others.
  3. Tick 2: message 2 and message 3 finished; consumer 2 takes message 4, consumer 3 takes message 5. A consumer that finishes early simply takes more, so one slow message does not hold the others up.
  4. Tick 3: message 1 and message 5 finished; consumer 1 takes message 6, consumer 3 takes message 7. That empties the queue; the consumers now finish what they hold.
  5. Tick 4: message 7 finished. Consumer 2 is still on message 4, which takes 3 ticks; nothing else waits for it.
  6. All 7 messages processed, each by one consumer (2 by consumer 1, 2 by consumer 2, 3 by consumer 3). Add consumers to drain faster; that is how a queue scales out.

Publish/subscribe

  • Producers publish to a topic; every subscriber gets its own copy.
  • The publisher does not know who listens; add a subscriber without changing it.
  • Use it for events: "order placed" feeds email, analytics and search.
Order serviceTopic: ordersEmailreceived 3Analyticsreceived 3Search indexreceived 3e1e1e1e2e2e2e3e3e3
Publish/subscribe: every subscriber gets a copy of every event.
  1. The order service publishes events to a topic. It does not know who listens; three services have subscribed.
  2. Event e1 reaches the topic, while e2 is about to be published. The topic keeps it for every subscriber.
  3. Every subscriber gets its own copy of e1: email, analytics and the search index each act on it independently. e2 reaches the topic.
  4. e2 fans out the same way; each subscriber reads at its own pace and keeps its own place in the stream. e3 reaches the topic.
  5. e3 is copied to all three subscribers too. Unlike a queue, where one consumer would take it, one published event here does three jobs.
  6. Each subscriber received all 3 events (3, 3, 3). A new subscriber can join without the publisher changing a line.

Acknowledgements and redelivery

  • A delivered message is hidden, not deleted, for a visibility timeout.
  • The consumer acks after the work; the ack deletes it.
  • No ack in time (crash, timeout) → the message is delivered again.
  • That is at-least-once delivery, so consumers must be idempotent.
QueueConsumerrunningPayments DBcharges for #42: 1processed ids: { m1 }deliveries: 2m1acked → deleted
Acks and redelivery: a crash before the ack means the message comes back.
  1. Message m1 says "charge order #42". The consumer will charge the card, then acknowledge, and only an ack deletes a message from the queue.
  2. The queue delivers m1 and hides it from other consumers for a visibility timeout. It stays in the queue until it is acknowledged.
  3. The consumer charges the card: charges for #42 = 1. It records m1 as processed in the same database write.
  4. The consumer crashes before sending the ack. The work is done, but the queue cannot know that; m1 is still in flight.
  5. The visibility timeout expires with no ack, so m1 becomes visible again. This is at-least-once delivery: a message may arrive more than once.
  6. A restarted consumer receives m1 again: delivery number 2. Without a check it would charge #42 a second time (2 charges).
  7. The consumer finds m1 in its processed ids and skips the charge, so it stays at 1. Handling a message twice has the same effect as once: the consumer is idempotent.
  8. Now it acks, and the queue deletes m1. Two deliveries, one charge: at-least-once delivery plus an idempotent consumer gives an exactly-once effect.

Delivery guarantees

At-most-onceAt-least-onceExactly-once effect
HowAck before the workAck after the workAt-least-once + idempotent consumer
RiskLost messagesDuplicatesMore work to build
Typical useMetrics you can dropMost jobsPayments, orders

Dead-letter queues

  • Retries fix passing faults (a timeout, a busy database), ideally with backoff.
  • A poison message fails every time; retrying forever blocks the queue.
  • After N failed deliveries it moves to a dead-letter queue for inspection and replay.
  • Alert on DLQ depth: it means something is broken.
QueueDead-letter queueConsumerwaitingm7m7 failed 3 of 3
Dead-letter queue: a message that keeps failing is set aside after N tries.
  1. The consumer takes messages from the head of the queue. m7 has a malformed payload that makes the consumer fail every time; the queue allows 3 deliveries per message.
  2. Delivery 1 of m7 fails and is not acknowledged, so the message goes back to the queue to be tried again.
  3. m7 is back at the head of the queue with its delivery count at 1. Many queues wait a little longer before each retry (backoff).
  4. Delivery 2 of m7 fails as well. Retrying helps with a passing fault, such as a timeout, but not with a bad payload.
  5. m7 returns to the head again, count 2. One more failure and the queue gives up on it.
  6. Delivery 3 fails too: m7 has used all 3 attempts.
  7. After 3 failed deliveries m7 is moved to the dead-letter queue instead of being retried forever, and the main queue moves on.
  8. m8 is processed and acknowledged. A poison message no longer blocks the queue: m8 went through right behind it.
  9. The queue is clear and m7 waits in the dead-letter queue with its error, for someone to inspect, fix and replay. Nothing was lost and nothing looped forever.

Ordering and tools

  • A single queue can be FIFO, but competing consumers break strict order; order is usually kept per key (a partition or message group).
  • RabbitMQ, Amazon SQS: a message is deleted once acked.
  • Kafka: a retained, partitioned log; each consumer group tracks its own offset and can replay.

Interview questions

  • Why put a queue between two services? To decouple them, absorb bursts and let the consumer fail or scale without losing work.
  • Queue or pub/sub for "order placed"? Pub/sub: several services each need the event.
  • A consumer crashes mid-message. What happens? No ack arrives, so after the visibility timeout the message is redelivered.
  • Why must consumers be idempotent? At-least-once delivery can repeat a message; processing it twice must not repeat the effect.
  • What is a dead-letter queue for? Holding messages that keep failing, so they stop blocking the queue and can be fixed and replayed.

Next: Rate Limiting · Before this: Consistent Hashing

Common questions

Why use a message queue instead of a direct API call?

A direct call needs the receiver up and fast enough at that moment. With a queue the producer only needs the queue: the consumer can be slow, restarting or briefly down, and work waits instead of failing. It also lets you add consumers to drain work faster.

What is the difference between a queue and pub/sub?

In a point-to-point queue each message is processed by one of the competing consumers, which spreads the work. In publish/subscribe each subscriber gets its own copy of every message, so one event can update search, send an email and feed analytics.

What does at-least-once delivery mean?

The queue guarantees a message is not lost, but it may be delivered more than once: if a consumer crashes after doing the work but before acknowledging, the message is redelivered. Consumers must therefore be idempotent, so a second delivery has no extra effect.

How do you make a consumer idempotent?

Give each message a unique id and record processed ids in the same transaction as the work, skipping any id already recorded. Or make the operation naturally idempotent, such as setting a status instead of incrementing a counter, or using an idempotency key with the payment provider.

What is a dead-letter queue?

A separate queue where messages go after failing a set number of deliveries. It stops one bad message (a poison message) from being retried forever and blocking others, and keeps it with its error so someone can inspect it, fix the cause and replay it.

Is Kafka a message queue?

Kafka is a distributed log rather than a classic queue. Messages are kept for a retention period instead of being deleted when consumed, and each consumer group tracks its own offset, which gives pub/sub across groups and competing consumers within a group, one consumer per partition. RabbitMQ and SQS delete a message once it is acknowledged.

← Consistent Hashing · Rate Limiting →