Skip to main content

Message Queues

A message queue sits between a producer and a consumer and holds messages until a consumer is ready to process them, delivering each message to exactly one consumer. Where Pub/Sub fans one event out to every subscriber, a queue distributes a stream of work across a pool of consumers, so each unit of work is handled once — the queue is the standard tool for "many jobs, a pool of workers," while pub/sub is the standard tool for "one event, many independent reactions."

Decoupling speed, not just presence​

Pub/sub decouples who needs to know about an event. A queue decouples something different and just as valuable: how fast the producer and consumer each run. A producer can enqueue work far faster than a slower consumer can process it, and the queue absorbs that difference by buffering — the producer never blocks waiting for the consumer, and the consumer works through the backlog at its own sustainable pace instead of being overwhelmed by a traffic spike directly.

System Design Lab

This is the same decoupling idea behind Caching 101, just applied to work instead of data: a cache absorbs a mismatch between how often data is read and how expensive it is to compute; a queue absorbs a mismatch between how fast work arrives and how fast it can be done. Adding more consumers to drain a queue faster is a direct, simple application of horizontal scaling to the consumer side specifically.

At-least-once delivery — and why that means idempotency​

A queue's core reliability promise is usually at-least-once delivery: a message isn't removed from the queue until a consumer explicitly acknowledges finishing it, so if a consumer crashes or times out mid-processing, the un-acknowledged message is redelivered — possibly to a different consumer. This is a deliberate, safe default (better to risk processing a message twice than to silently lose it), but it has a direct consequence worth naming explicitly: consumers must be idempotent, in exactly the sense covered in Idempotency — processing the same message twice has to produce the same result as processing it once, because "twice" is always a real possibility, not an edge case. A payment-processing consumer that isn't idempotent, fed by an at-least-once queue, will eventually double-charge someone.

Backpressure and what happens when the queue itself fills up​

Buffering has a limit — a queue is a real, finite resource (memory or disk), and if producers keep enqueuing faster than consumers can drain for long enough, the queue itself grows without bound. Backpressure is the general term for a system's response to this: slowing down or rejecting the producer once the queue is sufficiently full, rather than letting it grow unboundedly and eventually run out of memory or degrade every consumer's performance. Designing what happens under backpressure — reject new writes, block the producer, drop the oldest messages — is as much a part of a queue-based design as picking the queue technology itself.

Ordering: another guarantee that isn't automatic​

Consumers pulling in parallel from the same queue means messages can be processed out of the order they were produced, unless the queue specifically preserves per-key ordering (e.g. all messages for a given order_id always going to the same consumer, so they're processed in sequence relative to each other, even though messages for different keys can interleave freely). Whether ordering matters — and at what granularity — is workload-specific: a stream of independent notification jobs usually doesn't care about ordering at all, while a stream of "debit, then credit" events for the same account very much does.

Why this matters in an interview​

Reaching for a queue specifically when the problem is "smooth out a burst of work across a pool of workers" — rather than "notify several independent services," which is pub/sub's job — is the core distinction worth getting right. Beyond that, proactively naming at-least-once delivery and the idempotency it requires on the consumer side is exactly the kind of failure-mode thinking that separates "we'll use a queue" from a design that's actually been reasoned through.

Message queue vs. pub/sub: pros and cons​

Message queue

  • Each message is handled by exactly one consumer — a natural fit for distributing work
  • Buffers bursts so a slow consumer pool doesn't get overwhelmed directly
  • Backpressure gives an explicit, controllable response to sustained overload

Pub/sub

  • Plain fan-out delivers to every subscriber — no built-in way to share one job across a worker pool
  • No natural backpressure story for a single overloaded consumer; it just keeps receiving everything published
  • Not the right default when the actual need is "share out this work," not "notify every independent service"

Further Reading​

Saved locally in your browser — visible in the sidebar as you go.