Sign in
TodayMapLearnPracticeReview
Library
16 MINcoreScaling & PerformanceNot started

Message Queues

A message queue decouples producers from consumers. Producers write messages to a queue; consumers read them at their own pace. This smooths traffic spikes, enables async processing, and lets producers and consumers scale independently.

Why this matters

Without a queue, a traffic spike to your service causes it to synchronously call downstream services, which also spike, which cascade into failure. A queue absorbs the spike — producers write quickly and move on, consumers process at a steady rate. This is the single most important pattern for building resilient, scalable backends.

Prerequisites
  • Task Queues
Related
  • Publish/Subscribe (Pub/Sub)
  • Event-Driven Architecture
  • Competing Consumers
  • Back Pressure
Used in
  • Async Request-Reply
  • Claim Check
  • Design Chat System
  • Design Notification System
  • Message Queues (Async)
  • Pipes & Filters
  • Priority Queue
  • Queue-Based Load Leveling
  • Sequential Convoy
  • Task Queues
Lesson

How it works

A message queue is a buffer between producers and consumers. Producers write messages to the queue; consumers read them asynchronously. The key insight: the producer doesn't wait for the consumer to process the message — it writes and moves on.

This decoupling has three benefits:

  1. Smoothing: if producers burst, the queue absorbs the spike and consumers process at a steady rate.
  2. Decoupling: producers and consumers can scale independently.
  3. Reliability: if a consumer crashes, messages stay in the queue (or are redelivered) — no data loss.

Message delivery semantics:

  • At-most-once: messages may be lost but never duplicated. Simple, fast, unreliable.
  • At-least-once: messages are never lost but may be duplicated. Requires idempotent consumers. Most common.
  • Exactly-once: messages are delivered once and only once. Expensive to achieve (requires distributed consensus). Often simulated with at-least-once + idempotency.

Most systems use at-least-once + idempotency. This is why idempotent operations (the previous lesson) are so important — they make at-least-once delivery safe.

The exactly-once myth

True exactly-once delivery across a distributed system is impossible without giving up availability (FLP impossibility). What systems like Kafka call 'exactly-once' is actually at-least-once + transactional idempotency — the consumer deduplicates using a message ID. This is good enough for almost all use cases.

Common queue patterns:

  • Work queue: one queue, multiple consumers (competing consumers). Each message is processed by exactly one consumer. Used for background jobs (send email, process image).
  • Pub/sub: one message, multiple subscribers. Each subscriber gets a copy. Used for event notifications.
  • Request-response: the producer sends a message and waits for a reply (via a reply queue). Used for async RPC.
  • Dead-letter queue: messages that fail processing N times are moved to a DLQ for inspection. Prevents poison messages from blocking the queue.
Check yourself
interview

Your e-commerce site has a flash sale. Traffic spikes 100x for 2 minutes. Without a message queue, what happens?

Pick one answer.

Check yourself
core

A message queue delivers messages at-least-once. Your consumer processes a 'charge credit card' message. What must you do?

Pick one answer.

SemanticAt-most-onceAt-least-onceExactly-once (claimed)
Message lossPossible — no retransmissionImpossible — retransmit on no-ACKImpossible
Message duplicationImpossiblePossible — retransmission creates duplicatesImpossible (in theory)
CostLowest — fire and forgetMedium — track ACKs, retransmit on timeoutHighest — distributed consensus + transactional dedup
Consumer requirementNoneIdempotent — handle duplicates safelyNone — broker dedupes
Real systemsStatsD, fire-and-forget logging, UDP telemetryKafka default, SQS standard, RabbitMQ defaultKafka transactions (idempotent producer + transactional consumer), SQS FIFO with dedup ID (per-message exactly-once within 5-min window)
What 'exactly-once' really means——At-least-once delivery + idempotent consumer + dedup by message id — the broker makes duplication impossible to OBSERVE, even though it can still happen internally
Use caseTelemetry, metrics, sampling where loss is OKMost production workloads — payments, emails, jobsStateful stream processing where duplicates would corrupt aggregates
Delivery semantics comparison. 'Exactly-once' is almost always at-least-once + idempotency + deduplication under the hood.
System design fundamentals— Supplementary explanation. The NO CAP lesson remains self-contained.

Real example: Kafka at LinkedIn.

Kafka was built at LinkedIn in 2010-2011 to solve a specific problem: their existing messaging infrastructure (ActiveMQ) couldn't handle the throughput required to stream every user-action event (page views, clicks, profile updates, job applications) from the website to their Hadoop data warehouse for analytics. ActiveMQ was doing ~100K messages/sec cluster-wide; LinkedIn needed ~2M/sec and growing.

Kafka's design choices (documented in the original 2011 NetDB paper and LinkedIn's engineering blog):

  • Append-only log partitioned by key — each partition is an ordered, immutable sequence of messages on disk. Writes are sequential (fast even on spinning disks), and consumers read at their own pace by tracking an offset. This is fundamentally different from traditional message queues (RabbitMQ, ActiveMQ) which delete messages after ACK.
  • Consumer groups — multiple consumers can read the same partition by splitting the partitions among them (one partition per consumer in a group, max). Different consumer groups read independently. This decouples producers from consumers: a single Kafka topic can feed a real-time fraud-detection system, a batch analytics pipeline, and a search indexer — all reading the same stream at their own pace.
  • Replication across brokers — each partition has N replicas; one is leader, the rest follow. If a broker dies, a replica takes over as leader. This is the durability story that lets you run Kafka on commodity hardware.
  • Retention by time or size, not by ACK — messages stay in Kafka for hours, days, or weeks regardless of whether consumers have read them. This enables replay: a new consumer can re-process the entire history of a topic. (Trade-off: storage cost.)

By 2014, LinkedIn was processing trillions of messages per day through Kafka across thousands of topics. By 2024, Kafka is the standard for event streaming at almost every large company — Uber, Netflix, Twitter, Airbnb, LinkedIn all run it. The lesson: a single durable, replayable log is a more flexible primitive than a delete-after-ACK queue. Most modern event-driven architectures are built on this insight.

Poison messages — the silent queue killer

A poison message is one that always fails processing (malformed payload, missing reference, division by zero). Without a max-retry limit and DLQ, the consumer retries forever, the queue grows, every other message is blocked behind the poison, and the entire pipeline stalls. Production rule: always set max retries (typically 3-5) with exponential backoff, then route the message to a dead-letter queue and alert on DLQ depth. The DLQ is your queue's immune system — it isolates the broken message so the healthy ones can flow. Without it, one bad message can take down the whole system.

Check yourself
interview

Your email-sending consumer reads from an SQS queue. A message with malformed JSON arrives, your parser throws, the message is retried 100 times over 4 hours, blocking the queue for legitimate emails. Users complain emails are delayed by hours. What is the correct production architecture to prevent this?

Pick one answer.

Kafka vs RabbitMQ — the choice that defines your architecture.

Both are 'message queues', but they have fundamentally different models and trade-offs. Choosing wrong is expensive to undo.

RabbitMQ is a classic message broker. Messages live in queues; consumers ACK each message; ACKed messages are deleted. Routing is rich (topic exchanges, fanout, header-based) and per-message. Latency is low (sub-millisecond on LAN). The model is work distribution: 'I have N tasks, distribute them across M workers, each task done once.' Use RabbitMQ when you want a classic job queue (send email, process upload, run background job) with rich routing.

Kafka is a distributed append-only log. Producers write to topic partitions; consumers read at their own offset. Messages are NOT deleted on ACK — they're retained by time or size (hours to weeks). Multiple consumer groups can read the same message independently. The model is event streaming: 'I have a stream of events; multiple downstream systems want to react to each event, and replay is valuable.' Use Kafka when you want event-driven architecture (every user action becomes an event), analytics pipelines (consume the same stream into a data warehouse), or replay capability (re-process last week's events after fixing a bug).

The choice:

  • Job queue pattern (send email, run cron task, process upload) → RabbitMQ. Messages should be consumed once and deleted. Routing by message attribute matters.
  • Event streaming pattern (user activity → analytics + notifications + search index) → Kafka. Multiple consumers, each wants the full event history, replay matters.
  • Mixed → use both. Many real systems have RabbitMQ for job queues and Kafka for event streaming.

A common mistake: choosing Kafka because 'it's more scalable' when the workload is actually a job queue. Kafka CAN do job queues (consumer group with one partition per consumer), but it's overkill — you'll pay the operational cost of running a Kafka cluster (3+ brokers, ZooKeeper/KRaft, monitoring) for functionality RabbitMQ gives you in one process. Conversely, using RabbitMQ for high-throughput event streaming will hit the wall when you need multiple independent consumers or replay — Kafka's log retention model is what makes those possible.

LinkedIn's choice of Kafka (and Uber's, Netflix's, Twitter's) wasn't because Kafka is 'better' — it's because their workload was event streaming at scale, where the append-only log with retention is the right primitive. A 100-message-per-second job queue in a small startup should not run Kafka; it should run RabbitMQ or even a database-backed queue.

Engineering mental model

Mental model. Think of Message Queues as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Message Queues mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”

Design lens

Before choosing Message Queues, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.

Original NO CAP systems visual for Message Queues.
Image unavailable. Original NO CAP systems visual for Message Queues.
Message Queues: a compact system-thinking visual.— Original NO CAP visual.
message_id = queue.publish({
    "type": "message-queues",
    "key": resource_id
})
# Consumer must be safe to retry.
A minimal engineering sketch for reasoning about Message Queues.

Back-of-the-envelope reasoning

Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.

Interactive sandboxdeterministic

Interactive thought experiment: Message Queues

Change the variables below and predict what breaks first in Message Queues. The production lab can later reuse these same inputs.

System pressure6%
Queue backlog growthstable
Try this

Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.

Hint

If you are stuck on Message Queues, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.

Check yourself
solid

You increase traffic by 10× in a system using Message Queues. What should you inspect first?

Pick one answer.

Check yourself
interview

Which statement is the safest engineering habit when using Message Queues?

Pick one answer.

Try this
interview

You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.

Production scenario: your system uses Message Queues, traffic suddenly spikes, and p99 latency doubles. What is your first move?

Interview drill

Answer this without notes: When would you choose Message Queues, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.

Engineering lens

For Message Queues, separate producer speed from consumer speed. The key design question is what happens when production temporarily exceeds processing capacity: queue it, shed it, slow producers down, or degrade the feature.

Numerical sanity check

A simple queue sanity check: if producers create 8,000 messages/s and consumers process 6,000 messages/s, backlog grows at roughly 2,000 messages/s until the imbalance is corrected.

Check yourself
interview

Do not optimize for a memorized definition. Reason from the workload and failure mode.

A downstream service slows down while Message Queues keeps accepting traffic. Would you rather apply backpressure, queue more work, shed load, or degrade? Explain the trade-off.

Pick one answer.

Trade-offs

What you gain, what you pay

Pros
  • +Smooths traffic spikes — producers write fast, consumers process steadily.
  • +Decouples producers from consumers — they scale independently.
  • +Reliable — messages persist in the queue even if consumers crash.
  • +Enables async processing — producers don't block on slow operations.
Cons
  • −Adds latency — messages are processed asynchronously, not immediately.
  • −Adds infrastructure — another system to deploy, monitor, and debug.
  • −Requires idempotent consumers — duplicate delivery is normal.
  • −Operational complexity — dead-letter queues, retries, ordering guarantees.
Failure modes

How this breaks in production

  • Poison messages — a message that always fails processing blocks the queue (mitigated by DLQ + max retries).
  • Queue buildup — consumers can't keep up; queue grows unbounded (mitigated by back-pressure + auto-scaling).
  • Message ordering — in distributed queues, FIFO is expensive and often not guaranteed across partitions.
  • Consumer crash mid-processing — message may be redelivered (mitigated by idempotency).
Common mistakes

Don't fall into these traps

  • •Assuming exactly-once delivery — it's almost always at-least-once.
  • •Not handling duplicate messages — every consumer must be idempotent.
  • •Using a queue when a synchronous call would do — adds unnecessary complexity.
  • •Forgetting to monitor queue depth — a growing queue is the first sign of trouble.
Where you see it

Real systems using this

Every async backend (email, notifications, image processing).Every event-driven microservice architecture.Every system that handles traffic spikes (flash sales, viral content).
Teardowns

How real systems implement this

  • RabbitMQ — Classic message broker with rich routing, acknowledgments, and dead-letter exchanges. At-least-once delivery with optional transactions.
  • AWS SQS — Managed message queue with at-least-once delivery, dead-letter queues, and visibility timeouts. Standard (unordered, at-least-once) and FIFO (ordered, exactly-once via dedup ID) modes.
Interview prompts

Practice saying it out loud

  • Q1What is a message queue? How does it differ from a pub/sub system?
  • Q2What are the delivery semantics? Why is exactly-once hard?
  • Q3How do you handle duplicate messages in a consumer?
  • Q4When would you use a queue vs a synchronous call?
Research

Further reading & references

System Design Primer
Open source
ByteByteGo — Scale from zero to millions
ByteByteGo
System Design Tutorial
GeeksforGeeks
System Design Roadmap
roadmap.sh
Scaling & Performance reference
Reference
Scaling & Performance reference
Reference
Scaling & Performance reference
Reference

Core explanations are original NO CAP material. External references are provided for deeper study and standards.

What next?

Mark as understood once the mental model clicks.

Next recommended

Publish/Subscribe (Pub/Sub)