Skip to content

Message Queues vs Streams

Q: What is a message queue and how does it work?

A message queue (e.g. SQS, RabbitMQ) is a buffer between producers and consumers. A producer enqueues a message; a consumer dequeues and acknowledges it; the broker then deletes the message. Once acked, it's gone.

This delete-on-ack semantic is the defining property: a queue models work to be done — each task is owned by exactly one consumer. Competing consumers share the queue's message load; adding workers scales throughput linearly until the queue itself is the bottleneck. SQS standard queues provide best-effort ordering; FIFO queues add ordering guarantees at ~300 msg/s throughput (vs ~3,000 for standard).

Q: What is a log-based stream and what makes it different?

A stream (e.g. Kafka, Kinesis, Pulsar) is an append-only, durable log. Messages are retained for a configurable period regardless of whether they've been read. Each consumer group tracks its own offset — reading does not remove the message.

Key implications:

  • Fan-out without duplication: multiple independent consumer groups each read the full log at their own pace. A new group can start reading from offset 0 and replay all history.
  • Ordering is per-partition, not global: Kafka guarantees strict order within a partition but not across partitions. Messages are routed to partitions by key (or round-robin); all messages for the same key go to the same partition, preserving per-entity order.
  • Partition count is nearly irreversible: Kafka allows adding partitions but not removing them, and re-partitioning redistributes key assignments — which breaks per-key ordering and forces consumer group rebalancing. Size your partitions conservatively and plan ahead.
Q: When should you choose a queue over a stream?
CriteriaQueueStream
Primary modelWork distributionEvent log / pub-sub
After consumptionMessage deletedMessage retained
Multiple consumersCompete for the same messageEach group gets all messages independently
Replay neededNoYes
Ordering requiredPartial / FIFO optionPer-partition guarantee
Backlog visibilityQueue depth metricConsumer lag metric
ExamplesSQS, RabbitMQ, Azure Service BusKafka, Kinesis, Pulsar

Use a queue for tasks and commands — things done exactly once by one worker, where you want automatic work distribution. Use a stream for events that multiple systems need to observe independently, where auditability or replay are needed, or where you need a durable audit log.

The interviewer's gotcha: "Can I use Kafka like a queue?" — yes, with a consumer group of one consumer per partition, but you lose easy scaling and automatic competing-consumer balancing.

Q: What is backpressure and how do queues and streams handle it?

Backpressure occurs when producers emit faster than consumers can process.

  • Queues: messages accumulate (bounded by storage). Visibility timeouts prevent a slow consumer from blocking others. Monitor queue depth — a growing queue is the primary signal. At extreme depths, storage costs rise and time-to-process grows, which breaks SLA for time-sensitive tasks.
  • Streams: consumer lag grows (offset falls behind the log head). Kafka retains data up to a retention limit (time or bytes); a lagging consumer that falls off the retention window permanently loses messages. Monitor consumer group lag (max lag across partitions). The danger is silent: no error, messages just disappear from the consumer's perspective once the retention window rolls past.

Application-level backpressure strategies: rate-limiting producers, applying flow control at the consumer (pause/resume), or scaling consumer replicas when lag exceeds a threshold.

Q: What is a Dead-Letter Queue (DLQ) and what are the operational considerations?

A DLQ is a secondary queue/topic where messages that fail processing after N retries are routed, preventing poison messages from blocking the main queue indefinitely.

Typical flow: message fails → retry up to max attempts → move to DLQ → alert on DLQ depth → engineer investigates and either reprocesses or discards.

Operational gotchas:

  • DLQ growth is a symptom, not a solution. Unmonitored DLQs silently accumulate data loss.
  • Set up alerting on DLQ depth immediately when you enable the DLQ.
  • For Kafka, a "dead-letter topic" is application-managed — the broker doesn't move messages automatically; your consumer must catch failures and produce to the DLQ topic itself.
  • When replaying from a DLQ, confirm the consumer handles the message idempotently — the bug that caused the original failure may have partially applied effects.

Common follow-ups

  • How does Kafka's consumer group rebalancing work and what causes partition reassignment?
  • What is the difference between a topic and a partition in Kafka?
  • How would you implement fan-out with SQS (hint: SNS + SQS)?
  • How do you handle duplicate messages in a competing-consumer queue?