ayushsalampuriya.xyz Revise

System Design · Core

Message Queues

Buffer and decouple work: why queues exist, ACKs, delivery guarantees, Kafka vs SQS vs RabbitMQ, and dead-letter queues.

In simple words: A queue lets producers hand off work without waiting for consumers. It absorbs bursts and supports retries, but consumers must handle duplicates and backlog growth.

Concrete example: After an Instagram photo upload, the API stores the image and publishes a processing job. Separate workers resize it, scan it, and create thumbnails without keeping the user request open.

Short version: HLD Message Queues.

1. Why queues

A queue sits between producers and consumers so they do not have to be up, fast, or scaled together. Classic wins: absorb spikes, retry safely, and fan out work to worker pools.

Sync pain: Upload API waits for resize + virus scan + notify → slow, fragile With queue: Upload API writes object + enqueues "process:photoId" Workers pull jobs at their own pace API returns 202/200 quickly

2. Core actors

Producer → [ Queue / Topic ] → Consumer(s) | optional DLQ

3. Acknowledgements (ACKs)

1) Consumer receives message (not deleted yet) 2) Processes work 3) Sends ACK → broker deletes / advances offset If crash before ACK → redelivery after timeout / rebalance

Visibility timeout (SQS) or unacked redelivery (Rabbit) or offset commit (Kafka) are variants of the same idea: do not drop work on failure.

4. Delivery guarantees

GuaranteeMeaningPractice
At-most-onceMay lose; no dupMetrics where loss is OK
At-least-onceNo loss; may dupDefault; need idempotent consumers
Exactly-onceProcess once end-to-endHard; often “effectively once” via idempotency + dedupe
Non-idempotent: balance += 10 Idempotent: apply txn_id T once; store processed_ids

5. Ordering

6. Partitioning and consumer groups

One queue or partition has a throughput limit. Split a topic into partitions so several consumers can process messages in parallel. Within one consumer group, each partition is assigned to at most one active consumer at a time.

Topic: photo-jobs Partition 0 → Consumer A Partition 1 → Consumer B Partition 2 → Consumer C If the group has 5 consumers but only 3 partitions, 2 consumers are idle.

Choose a partition key that preserves the order you need and still spreads traffic. For a payment stream, account_id keeps one account's events in order. A low-cardinality key such as city can create a hot partition when one city produces most of the traffic.

7. Back pressure and backlog

A queue delays overload; it does not create processing capacity. If producers publish 300 jobs per second and consumers finish 200, the backlog grows by 100 jobs per second. Eventually retention, disk, or the business latency limit is exhausted.

8. Kafka vs SQS vs RabbitMQ

KafkaSQSRabbitMQ
ModelLog / topics / partitionsManaged queueSmart broker, exchanges
ReplayYes (retain by time/size)No (delete on ACK)Limited
Fan-outConsumer groupsSNS+SQS or multiple queuesExchanges → queues
OpsHeavier (or managed MSK)Serverless-ishModerate
Best forEvent streams, high throughputSimple async jobs on AWSRouting, protocols, classic MQ

Interview default: Kafka for high-volume event streams and replay; SQS for straightforward task queues on AWS; Rabbit when you need flexible routing patterns.

9. Dead-letter queues (DLQ)

Main queue --fail N times--> DLQ Ops inspects / replays / alerts Poison message does not block the partition forever

10. Anti-patterns

11. Sketch for designs

Draw producer → topic/queue → consumer group → DB side effects, plus DLQ. Call out partition key, ACK/idempotency, and how you scale consumers.

Quick revision

  • Queues decouple, buffer spikes, and enable retries.
  • ACK after success; crash before ACK → redelivery.
  • At-least-once + idempotent consumers is the practical combo.
  • Kafka = log/replay/partitions; SQS = managed tasks; Rabbit = routing MQ.
  • Order per partition key, not globally across the cluster.
  • Consumer parallelism is limited by partition count within a group.
  • A growing backlog needs scaling or back pressure; a queue is only a buffer.
  • DLQ for poison messages; alert and replay.
  • Queue pointers to big payloads, not the payloads themselves.