10 min read
System Design Index
Start Here
Tier 1 -- Building Blocks
Scale Reads
Scale Writes
Database Selection
Traffic Control
Consistency & Coordination
Estimation
Tier 2 -- Core Systems
Tier 3 -- Location & Real-Time
Tier 4 -- Infrastructure & Data
Tier 5 -- Finance & Commerce
Tier 6 -- Advanced & Collaborative
System Design Index
Start Here
Tier 1 -- Building Blocks
Scale Reads
Scale Writes
Database Selection
Traffic Control
Consistency & Coordination
Estimation
Tier 2 -- Core Systems
Tier 3 -- Location & Real-Time
Tier 4 -- Infrastructure & Data
Tier 5 -- Finance & Commerce
Tier 6 -- Advanced & Collaborative
Message Queues & Async Writes (Kafka, SQS, RabbitMQ)
Tier 1 — Building Block
1. What Is It?
A is a durable, ordered buffer that decouples the producer of work (the service that generates a message) from the consumer (the service that processes it). Producers write messages to the queue without waiting for processing to complete; consumers read and process messages independently at their own pace.
Without message queues, every service call is synchronous and tightly coupled: if Service B is slow, Service A blocks and its increases. If Service B crashes, Service A's request fails. If there's a burst of 100K requests in one second but Service B can only process 10K/sec, the excess is dropped. Message queues solve all three problems: they absorb bursts (buffering), decouple failure domains (if consumer is down, messages accumulate in the queue until it recovers), and enable async processing (producer gets an immediate acknowledgment; processing happens later).
Service A sends order requests to Service B for payment processing. Suddenly, Service B goes down due to a crash. Which behavior would you expect if a message queue sits between Service A and Service B?
2. How It Works
Core Model
A topic (or queue) is a named channel that messages are published to. Producers send messages to a topic; consumers read from it. Topics decouple producers from consumers — they don't need to know about each other.
Message Lifecycle
Kafka Architecture (Log-Based)
is fundamentally different from traditional queues: it is an immutable, append-only distributed log. Messages are never deleted on consumption — they are retained for a configurable period (e.g., 7 days). Multiple consumer groups can read the same topic independently, each maintaining their own offset.
Key concepts:
- Partition: The unit of parallelism. A topic's data is split across partitions. One consumer per partition per consumer group (parallelism = partition count).
- Offset: A message's sequential position within a partition. Consumers commit their offset after processing. On restart, consumption resumes from the committed offset.
- Consumer Group: A logical set of consumers that collectively consume a topic. Each partition is assigned to one consumer within the group. Multiple consumer groups consume the same topic independently.
- : Each partition has 1 leader + N-1 followers. Producers write to the leader; followers replicate. If the leader dies, a follower is elected leader.
A payment service and an analytics service both need to consume every order event from the same Kafka topic. If the topic has 6 partitions, which setup correctly allows both services to independently receive all messages?
3. Variants & Comparisons
| Apache Kafka | AWS SQS | RabbitMQ | |
|---|---|---|---|
| Model | Log-based (immutable, retain messages) | Traditional queue (delete on ACK) | Traditional queue (AMQP, routing rules) |
| Ordering | Per-partition (total order within partition) | Best-effort (FIFO queue available separately) | Per-queue |
| Replay | Yes — consumers rewind offset | No — once consumed + ACKed, gone | No (dead letter queue only) |
| Multiple consumers | Yes — independent consumer groups each read full topic | No — each message consumed by one consumer | No — each message consumed by one consumer (fanout via exchange) |
| Throughput | Very high (millions of msg/sec per cluster) | High (no stated limit, auto-scales) | Medium (~50K–100K msg/sec per node) |
| Latency | ~10ms typical (batch-optimized) | ~1–100ms | <10ms (lower than Kafka for single message) |
| Retention | Configurable (hours to years) | Max 14 days | Configurable (TTL per message) |
| Routing | Topic + partition key | Simple queue per-destination | Complex routing (exchanges: direct, fanout, topic, headers) |
| Ops overhead | High (ZooKeeper/KRaft, brokers, partitions) | Zero (fully managed) | Medium (managed or self-hosted) |
| Best for | Event streaming, audit log, fan-out to many consumers, replay | Decoupled async tasks (serverless, Lambda triggers) | Complex routing, request-reply, task queues |
Delivery Semantics
| Guarantee | Description | Implementation |
|---|---|---|
| At-most-once | Message delivered 0 or 1 times; possible loss | Fire and forget; no ACK |
| At-least-once | Message delivered 1+ times; possible duplicates | Re-deliver on timeout or NACK; consumer must be idempotent (processing the same message twice produces the same result as processing it once) |
| Exactly-once | Message delivered exactly once | Kafka: transactional producer + idempotent consumer; complex to implement end-to-end |
In practice: at-least-once + idempotent consumer is the most common production pattern. Exactly-once is expensive to implement correctly and rarely necessary if consumers are idempotent.
Your team builds an order-processing service where multiple downstream services (inventory, billing, analytics) each need to receive and independently process every order event. Which message queue characteristic makes Apache Kafka the strongest fit for this use case compared to AWS SQS?
4. When to Use It (and When NOT To)
Use Message Queues When:
- Write spikes: The queue absorbs burst traffic; consumers process at their own pace. Order placement, payment initiation, file processing — any operation where the producer is faster than the consumer.
- Decoupling services: Service A should not care if Service B is temporarily down. A queue buffers the messages until B recovers.
- Async processing: Send email, resize image, generate report — work that doesn't need to complete synchronously within the user-facing request.
- Fan-out: One event needs to trigger multiple consumers (e.g., "order placed" → payment service + inventory service + email service). topics enable one event to feed multiple consumer groups.
- Audit log / : 's immutable log is a perfect audit trail; replay events to rebuild state.
Decision triggers:
- "If a downstream service is slower than upstream and can't be sped up → add a queue to absorb the mismatch"
- "If a crash of Service B would cause Service A's requests to fail → add a queue"
- "If one event needs to trigger multiple independent services → use Kafka topic with multiple consumer groups"
Do NOT Use Message Queues When:
- Low is required: A queue adds ~10ms+ . For P99 < 5ms requirements (financial trading), direct RPC is better.
- Request-response pattern: If the caller needs the result synchronously, a queue is the wrong abstraction. Use gRPC or REST.
- Simple in-process communication: Don't over-engineer. A function call is faster and simpler than a queue for intra-service communication.
- You need within a transaction: "Publish message AND update database atomically" is hard — requires transactional . If you can't tolerate the complexity, keep operations synchronous.
Anti-patterns:
- Ignoring : If consumers can't keep up with producers, the queue grows without bound. Monitor and alert when it exceeds a threshold. If grows persistently, add consumer instances or shard the queue.
- Non-idempotent consumers with at-least-once delivery: Messages WILL be delivered more than once (network retries, consumer crashes after processing but before ACK). If your consumer doubles charges, sends duplicate emails, etc. — you have a bug. Always design consumers to be idempotent.
- No (DLQ): If a consumer crashes processing a specific message N times, without a DLQ the message loops forever (poison pill), blocking the rest of the queue. Always configure a DLQ.
- Using Kafka as a pure database: Kafka is a log, not a queryable database. "Find all orders for user X" is a scatter-gather across all partitions — use a real database for those queries.
A payment service processes charge requests and occasionally crashes mid-processing. The messaging system uses at-least-once delivery, meaning a message may be delivered more than once. Which design approach correctly handles this situation?
5. Real-World Usage
LinkedIn ( origin): was created at LinkedIn in 2010 to handle the activity feed pipeline — every page view, click, and profile update was an event. LinkedIn needed multiple downstream systems (search index, recommendations, ads targeting, analytics) to consume the same event stream independently. Traditional queues deleted messages on consume; Kafka's log-based model let each system replay from any offset. LinkedIn now processes over 7 trillion messages per day across their Kafka clusters.
Uber (Kafka + Flink for real-time pricing): Uber uses Kafka as the backbone for real-time dynamic pricing. Every GPS ping from every driver and rider (~1M messages/sec peak) flows through Kafka topics. A Flink layer consumes these topics to compute surge pricing, estimated arrival times, and supply/demand ratios in real time. The immutability of the Kafka log means Uber can replay events to debug pricing anomalies or test new pricing models against historical data.
Amazon (SQS for decoupling): Amazon's "Building Microservices" architecture uses SQS extensively to decouple services. The canonical example: when a customer places an order, the order service publishes an OrderPlaced event to an SQS queue. The inventory service, fulfillment service, and email service each have separate queues triggered by the event (via SNS fan-out to multiple SQS queues). If the email service is down during a deploy, orders continue processing and email notifications catch up when the service restarts.
A streaming analytics platform ingests GPS location events from millions of mobile devices. After deploying a new geofencing algorithm, engineers discover the previous 6 hours of pricing calculations were incorrect. Which message broker design characteristic would allow them to reprocess those historical events to validate the fix?
6. Interview Cheat Sheet
Key sentences to demonstrate depth:
- "Message queues decouple producers from consumers in three dimensions: time (process asynchronously), rate (absorb burst traffic), and (consumer downtime doesn't fail the producer)."
- "'s key insight: messages are never deleted on consume — it's a durable log. Multiple consumer groups independently consume the same topic, each replaying from their own offset. This is why is used for fan-out to multiple services."
- "At-least-once delivery is the practical default — messages may be redelivered on failure. Design all consumers to be idempotent: processing the same message twice produces the same result as processing it once."
- "Kafka scales with partition count: consumers = partition count per consumer group. If you need N× , add N partitions. But you can't reduce partitions after the fact — plan partition count with growth in mind."
- "The transactional solves the 'publish and write DB atomically' problem: write the event to an
outboxtable in the same transaction as the DB write, then a separate process tails the outbox table and publishes to Kafka. This prevents the 'write succeeded but publish failed' split-brain."
Common follow-up questions:
| Question | Concise Answer |
|---|---|
| "What is a consumer group in Kafka?" | A logical group of consumers that collectively consume a topic. Each partition is assigned to one consumer in the group. Adding consumers = more parallelism (up to partition count). Multiple groups consume independently — perfect for fan-out. |
| "How does Kafka guarantee ordering?" | Ordering is guaranteed within a partition. If you need total order for related events (e.g., all events for user X), use user_id as the partition key — all user X events go to the same partition in the same order. |
| "What happens if a consumer dies mid-processing?" | The message wasn't ACKed (committed offset not updated). After a timeout, Kafka reassigns the partition to another consumer, which re-reads from the last committed offset. The message is redelivered — hence at-least-once. |
| "Kafka vs. RabbitMQ — when to choose each?" | Kafka: high-throughput event streaming, replay, fan-out to multiple consumers, audit logs, stream processing. RabbitMQ: complex routing rules (AMQP exchanges), request-reply pattern, lower-throughput task queues where you need flexible message routing. |
| "What is the transactional outbox pattern?" | Write event to outbox table in the same DB transaction as your business data write. A separate relay process (or CDC tool like Debezium) reads the outbox table and publishes to Kafka. Guarantees exactly-once publish semantics without distributed transactions. |
Connections to other building blocks:
- & LSM Trees: Kafka's log storage is LSM-like: sequential append-only writes with periodic (log for use cases). Kafka's write is a .
- : Kafka partitions are of the log stream. The partition key determines which shard (partition) a message goes to, providing ordering within a shard.
- : Message queues naturally implement rate limiting by controlling consumer processing rate. The queue absorbs traffic spikes; consumers process at a controlled pace.
- Unique : Messages in Kafka are identified by (topic, partition, offset). For , producers often include a message ID (Snowflake or UUID) in the payload so consumers can deduplicate.
- / Cache: A is conceptually the write-path complement to a cache (which handles the read path). Writes go to queue (async processing); reads go to cache.
Glossary History
Click dotted jargon to save explanations here.
Glossary History
Click dotted jargon to save explanations here.