ayushsalampuriya.xyz Revise

HLD · Queues

Message Queues

A comprehensive study guide for system design interviews.

Message queues are fundamental components in distributed systems, serving as buffers that facilitate communication between different services. This note covers architecture, benefits, implementation strategies, and common technologies used in modern system design.

1. The Necessity of Message Queues

Consider a photo-sharing application like Instagram. When a user uploads a photo, the system must perform several time-intensive operations:

The limitations of synchronous architecture

In a simple synchronous design, the server performs all these tasks before returning a response. This approach suffers from three primary flaws:

  1. High latency: The user must wait for all background tasks to complete (e.g., 6+ seconds) before receiving a success confirmation.
  2. Fragility: If a single sub-service (like the filter service) crashes, the entire upload fails, and previously completed work (like resizing) may be lost.
  3. Inability to handle bursts: If traffic spikes from 50 to 5,000 uploads per second, a fixed-capacity server will fall over, leading to timeouts and dropped requests.

The asynchronous solution

By introducing a message queue, the server simply saves the file and writes a message (e.g., “Photo 456 needs processing”) to the queue. The server immediately responds to the user, while a pool of background workers (consumers) pulls messages from the queue to process the images at their own pace.

2. Core Concepts and Architecture

At its most basic level, a message queue is a buffer sitting between a producer and a consumer.

Kitchen analogy: A message queue functions like a ticket rail in a restaurant. The waiter (producer) places an order on the rail and moves to serve other tables. The cook (consumer) grabs the ticket when ready. The rail decouples the “front of house” from the “back of house.”

3. Internal Mechanisms and Reliability

Acknowledgements (ACKs)

To prevent data loss if a worker crashes mid-process, queues use ACKs:

Preventing duplicate processing

Systems use different methods to ensure multiple workers don't process the same message simultaneously:

4. Delivery Guarantees

In distributed systems, ensuring exactly how many times a message is delivered is a significant challenge.

Guarantee type Description Best use case
At-least-once Guaranteed delivery, but duplicates may occur. Requires idempotent consumers. Most common; standard for production and interviews.
At-most-once “Fire and forget.” Messages may be lost, but never duplicated. Metrics, analytics, or logs where occasional data loss is acceptable.
Exactly-once Every message is processed exactly one time. Extremely difficult; limited to specific ecosystems (e.g., Kafka internal patterns).

The importance of idempotency

Because at-least-once is the most practical guarantee, consumers must be idempotent—processing the same message twice produces the same result.

5. When to Use (and Avoid) Message Queues

Usage signals

  1. Async work: When the user does not need the result immediately (e.g., sending emails, generating reports).
  2. Bursty traffic: To smooth out load spikes by accumulating a backlog.
  3. Decoupling: When services have different hardware needs (e.g., upload servers need high throughput, while workers need GPUs).
  4. Reliability: When work must be persisted even if downstream services are temporarily offline.

Anti-patterns

Avoid queues for synchronous workloads with strict latency requirements (e.g., sub-500ms response times). Adding a queue introduces complexity and almost guarantees a breach of tight latency constraints.

6. Deep Dives: Scaling and Failure Handling

Partitioning and consumer groups

A single queue has throughput limits. To scale horizontally, queues are split into partitions.

Note: You cannot have more active consumers than partitions; extra consumers will sit idle.

Selecting a partition key

Choosing the right key is critical and involves a trade-off between ordering and distribution:

  1. Ordering: Messages with the same key go to the same partition. Vital for sequential tasks (e.g., a bank deposit must be processed before a withdrawal for the same account ID).
  2. Distribution: Keys should spread work evenly. Partitioning by “City” might create a hot partition for a large city like New York while smaller cities sit idle. Partitioning by a unique ID usually provides a more even spread.

Back pressure

A queue is a buffer, not a capacity solution. If producers consistently create 300 messages/second while consumers only process 200, the queue will grow indefinitely until it runs out of memory.

Solutions: Auto-scaling consumers, adding partitions, or applying back pressure (slowing down or rejecting requests from the producer).

Failure scenarios: poison messages and DLQs

A poison message is a malformed message that causes a consumer to crash every time it is processed. To prevent infinite retry loops:

7. Comparison of Common Technologies

Technology Characteristics Best for
Apache Kafka Distributed streaming platform; persists to disk; supports partitions and consumer groups. High throughput, durability, and scenarios requiring message replay.
AWS SQS Fully managed service. Offers “Standard” (best-effort ordering) and “FIFO” (strict ordering) queues. Simplicity and cloud-native AWS environments.
RabbitMQ Traditional message broker; supports complex routing via exchanges and bindings. Sophisticated message routing logic.

Note on Kafka durability

Kafka is unique because it persists messages to disk for a configurable retention window (e.g., one week). This allows for message replay, where a new or fixed consumer can re-process historical data if an error is discovered in previous processing logic.

Quick revision

  • Why: Decouple producers/consumers; absorb bursts; async work.
  • Core: Producer → queue → consumer; ACK before delete.
  • Guarantees: At-least-once is default — design idempotent consumers.
  • Scale: Partitions + consumer groups; consumers ≤ partitions.
  • Failures: Visibility timeout, poison messages → DLQ.
  • Pick tech: Kafka (replay/throughput), SQS (managed), RabbitMQ (routing).
  • Avoid when: Strict sync latency (<500ms) requirements.