AlgoMaster Logo

Message Queues

High Priority9 min readUpdated July 4, 2026
AI Mock Interview

Practice this topic in a realistic system design interview

Listen to this chapter
Unlock Audio

When one service calls another directly and waits, the caller is only as fast and as reliable as the service it called. If that service is slow or down, the caller feels it immediately.

A message queue breaks that direct wait. Instead of calling another service immediately, a producer puts a message into a queue. Later, one or more consumers read the message and do the work.

This fits work that can happen in the background: sending a welcome email after signup, resizing an image, processing a payment event, or smoothing out a traffic spike. The queue sits between producers and consumers as a buffer. It gives the system room to absorb bursts, retry failures, and scale workers separately.

Loading simulation...

But a queue does more than improve performance. It changes how the system behaves. Work may happen later. Messages may be retried. Duplicates can happen. Ordering becomes a design choice. Backlogs become something the team must watch.

This chapter covers the core parts of a message queue and the problems it introduces.

1. Core Components of a Message Queue

A message queue is built from a few parts that move work from the services creating it to the services handling it. The diagram shows how they connect before we look at each part.

Producer

The producer creates messages and sends them to the queue. A producer should not need to know which consumer will process the message or whether that consumer is running right now.

Examples: API server, checkout service, upload service, scheduled job.

Consumer

The consumer reads messages and does the work. Many consumers can read from the same queue at the same time.

Examples: email worker, payment worker, image processor, analytics pipeline.

Message

A message is one unit of work sent through the queue. It usually contains:

  • A payload, which is the business data, such as orderId, userId, or event details.
  • Metadata, which is extra information, such as message ID, timestamp, trace ID, priority, or retry count.

Messages should be small, clear, and versioned. Large files usually belong in object storage. The queue message should carry a reference to the file, not the whole file.

Queue

The queue stores messages until consumers can process them. Depending on the system, the queue may preserve order, support priorities, keep messages after they are read, or delete messages once they are acknowledged.

Broker

The broker is the system that owns queue storage and delivery. It accepts messages from producers, stores them, delivers them to consumers, tracks acknowledgments, and applies rules such as retries, retention, and dead lettering.

Examples include RabbitMQ, Amazon SQS, Google Cloud Pub/Sub, Azure Service Bus, and Redis Streams. Kafka is a distributed event log that is often used for queue-like workloads, but its model is different from a traditional broker.

2. How Message Queues Work

A message follows a predictable path after a producer hands it to the broker. The important step is acknowledgment: the consumer tells the broker, "I finished this message." That is what lets the broker recover when a consumer crashes halfway through.

alt[Consumer crashes before ACK]Store in queueRemove message or save progressProduce messageDeliver messageProcess workACK successDeliver again laterProducerBrokerConsumerProducerBrokerConsumer
7 / 7
algomaster.io

The basic flow looks like this:

  1. Produce: A service creates a message.
  2. Enqueue: The broker stores the message.
  3. Deliver: A consumer receives the message.
  4. Process: The consumer performs the work.
  5. Acknowledge: The consumer tells the broker the work succeeded.
  6. Remove or save progress: The broker deletes the message or records the consumer's progress.

Acknowledgment is the key reliability step. If the consumer crashes before acknowledging the message, the broker can deliver it again.

That retry behavior is useful, but it means consumers must assume the same message can arrive more than once.

3. Common Queue Patterns

Message systems support several patterns. Product names vary, but the design ideas are common.

Work Queue

A work queue spreads tasks across a group of consumers. Each message is normally processed by one consumer.

Use it when many workers can perform the same job, such as sending emails, generating reports, processing uploads, or running other background jobs.

Adding more consumers increases how much work you can process, until something else becomes the limit, such as the database, an external API, or a rate limit.

Publish/Subscribe

One published event can be copied to several independent subscribers. Each subscriber reacts in its own way.

In publish/subscribe, a producer publishes an event to a topic. Multiple independent subscribers can receive the event.

Use it when several parts of the system need to react to the same fact. An OrderPlaced event might update inventory, analytics, notifications, and fraud checks. A UserCreated event might trigger an onboarding email, CRM sync, and audit logging. A PaymentCaptured event might update billing, shipment, and customer history.

Pub/sub is useful because the producer does not need to know every service that cares about the event.

Priority Queue

A priority queue processes urgent messages before less urgent messages. Use it when some work should jump ahead, such as password reset emails before marketing emails, paid customer exports before free-tier exports, or fraud alerts before routine analytics tasks.

Priority can make lower-priority work wait longer, so it should be used carefully.

Delayed or Scheduled Queue

A delayed queue hides a message until a delay passes or a scheduled time arrives. Common uses include retrying later, checking trial expirations, sending reminder emails, and handling timeouts.

Not every broker supports this directly. Some systems implement it with scheduled jobs, delay topics, or separate retry queues.

Dead Letter Queue

When a message uses up its retries, it moves off the main queue into a separate place for investigation.

A dead letter queue stores messages that failed too many times. It keeps bad messages from blocking the main queue and gives engineers a place to inspect, fix, replay, or delete them.

4. Why Use Message Queues?

Message queues are useful when the producer and consumer should not have to move in lockstep.

Less Direct Dependency

The producer does not need to know which consumer handles the work. This makes it easier to change, scale, or restart consumers without changing producer code.

This does not remove the contract between systems. The message format becomes the contract.

Background Processing

The user-facing path can finish quickly while slower work runs in the background.

For example, an upload API can store the original file, enqueue an image-processing job, and return immediately. Workers can generate thumbnails after the request has completed.

Load Leveling

Queues absorb bursts. If 100,000 jobs arrive in one minute but workers can process only 10,000 per minute, the queue lets the system catch up over time instead of failing immediately.

This only helps when delayed processing is acceptable. If users need an answer right now, a queue does not remove that requirement.

Reliability Through Retry

If a worker crashes or another service times out, the message can be retried. This is one of the main reasons queues are used for important background work.

Retries need limits and monitoring. Infinite retries can turn one bad message into a permanent drain on worker capacity.

Independent Scaling

Producers and consumers can scale separately. If image uploads spike, add more image workers. If email volume drops, run fewer email workers.

The real limit may still be somewhere else. More workers will not help if all of them are waiting on the same database lock or third-party rate limit.

5. When to Use Message Queues

Use a message queue when background processing is acceptable and the system benefits from buffering, retry, or independent scaling.

Good fits include:

  • Background jobs: Email, image processing, report generation, video transcoding
  • Event-driven workflows: Several services reacting to the same business event
  • Traffic spikes: Buffering bursts that workers can process later
  • Unreliable services: Retrying work when another service recovers
  • Cross-service communication: Letting services work together without all being up at the same moment

Avoid adding a queue when:

  • The caller needs an immediate answer
  • The workflow requires strict validation before responding
  • The team has no plan to monitor backlogs
  • Ordering must be exact across many unrelated entities
  • A simple direct call would be easier and reliable enough

A queue can make a system more resilient, but it can also hide failure. A request may look successful because the message was accepted, while the actual work fails minutes later. The product must be designed for that delay.

6. Trade-offs and Failure Modes

Message queues solve real problems, but they introduce new ones.

Duplicate Processing

Most production systems should assume at-least-once delivery. In plain terms: a message may be delivered more than once if a consumer crashes after doing the work but before acknowledging it.

Consumers should be safe to retry. Processing the same message twice should not charge a customer twice, send duplicate refunds, or corrupt state.

Ordering Is Limited

Ordering is usually guaranteed only within a narrow boundary, such as a single queue, partition, message group, or key.

If strict ordering matters, group messages by the thing that must stay ordered, such as orderId or accountId. Global ordering rarely scales well.

Backlogs Can Become Incidents

A growing queue means consumers are falling behind. That may be fine for a short spike, but it becomes a production issue when messages get older than the business can tolerate.

Queue depth alone is not enough. Also track the age of the oldest message and how far consumers are behind.

Retries Can Amplify Failures

If another service is unhealthy, thousands of workers retrying aggressively can make recovery harder.

Use backoff, jitter, rate limits, and circuit breakers where appropriate. In simpler terms: retry more slowly, add randomness, and stop hammering a service that is already failing.

Message Contracts Need Discipline

Once other services consume your messages, changing the message format becomes a compatibility problem.

Use versioned formats, additive changes, and clear ownership. Do not remove or rename fields without a migration plan.

7. Best Practices

Running a queue well comes down to a handful of habits around message design, consumer behavior, and operations. They keep messages manageable, make retries safe, and make sure someone can recover the system when delivery goes wrong.

  • Keep messages small and clear: Put large files in object storage and send references through the queue.
  • Make consumers safe to retry: Use idempotency keys, unique constraints, or processed-message records.
  • Set retry and DLQ policies: Retry temporary failures, move repeatedly failing messages to a dead letter queue, and alert on important failures.
  • Monitor the right signals: Track publish rate, consume rate, queue depth, oldest message age, consumer errors, and retry rate.
  • Use backoff and jitter: Avoid sending too many retries at once after another service fails.
  • Choose ordering keys carefully: Preserve order where it matters, but do not force global ordering unless the business truly requires it.
  • Secure the queue: Encrypt sensitive message data, restrict producers and consumers, and audit access.
  • Plan for replay: Know whether old messages can be safely reprocessed after a bug fix or outage.
  • Document ownership: Every queue should have an owner, a purpose, retention settings, and an alert policy.

8. Popular Messaging Systems

Different systems make different trade-offs. Pick based on the workload, not just popularity.

SystemBest FitNotes
RabbitMQTraditional work queues and routingMature broker with exchanges, routing keys, acknowledgments, retries, and DLQ support. Good when you need careful routing rules.
Amazon SQSManaged cloud queuesSimple, highly scalable managed queue. Good for background jobs and separating AWS services. FIFO queues are available when ordering is needed within message groups.
Google Cloud Pub/SubManaged pub/sub and event intakeGood when one event should be sent to many services on Google Cloud. Uses topics and subscriptions instead of one classic queue.
Azure Service BusEnterprise messaging on AzureSupports queues, topics, subscriptions, sessions, scheduled delivery, and dead-letter queues.
Apache KafkaEvent streams and long-lived logsStores ordered records in partitions and lets consumers track where they are in the stream. Excellent for event streams and replay, but not a drop-in replacement for every simple task queue.
Redis StreamsLightweight stream processingUseful when Redis is already in the architecture and the workload fits how Redis stores and keeps data.

The most common mistake is choosing a system before defining the workload. A queue for email jobs, an event stream for analytics, and a workflow engine for multi-step business processes are related tools, but they are not the same tool.

Summary

Message queues let producers hand work to consumers for later processing. They help systems absorb bursts, retry failures, run background work, and scale producers and consumers separately.

The core trade-off is that work no longer happens immediately. You must design for delayed processing, duplicate delivery, partial failure, monitoring, and replay.

Use message queues when background work is acceptable and the buffer gives you real value. Keep the message contract clear, make consumers safe to retry, monitor backlogs, and treat the queue as a production system, not a hidden implementation detail.

Quiz

Message Queues Quiz

10 quizzes