Why Async At All
a buffer between producer and consumerA synchronous call ties the caller's fate to the callee's. If the downstream is slow, the caller waits; if it's down, the caller fails; if traffic spikes, the spike hits the callee at full force. A message broker inserts a buffer between them, and that buffer buys three things.
SYNCHRONOUS ASYNCHRONOUS
producer โโcallโโโถ consumer producer โโโถ [ queue ] โโโถ consumer
โ blocks on consumer โ returns now โ drains at
โ fails if consumer down โ โ its own pace
โผ โผ โผ
coupled: spike, slowness, and decoupled: queue absorbs the spike,
failure all propagate upstream survives consumer downtime, smooths load
- Spike absorption โ the queue soaks up a burst the consumer couldn't take head-on.
- Failure isolation โ a down consumer means a growing backlog, not a cascading outage.
- Load smoothing โ bursty writes become a steady drain at the consumer's comfortable rate.
- Eventual consistency โ the work happens later, so reads can race ahead of it.
- Harder debugging โ no single call stack; you trace a message across systems.
- Operational surface โ the broker is one more thing to run, size, and monitor.
Message Queues
work distribution, one consumer per messageA queue is point-to-point work distribution. Producers enqueue tasks; a pool of competing consumers pulls from the queue, and each message is handled by exactly one of them. Add consumers and throughput rises โ the queue is a natural load balancer for background work. This is the SQS / RabbitMQ model.
When a consumer picks up a message, it's hidden โ not deleted โ for a timeout window. If the consumer finishes and acks, it's removed. If the consumer dies first, the timeout lapses and the message reappears for someone else. This is how a queue survives worker crashes without losing work.
A message that keeps failing would retry forever, blocking the line. After N attempts it's shunted to a dead-letter queue for inspection instead of looping. The DLQ is where you find the poison messages, bad payloads, and downstream bugs.
Pub/Sub
one message, many subscribersWhere a queue delivers each message to one consumer, pub/sub fans out the same message to every subscriber. Producers publish to a topic; each subscriber gets its own copy. This is how one event drives many independent reactions without the producer knowing who's listening.
QUEUE (one wins) PUB/SUB (all get it)
โโโถ consumer A โโโถ email service
producer โโถ Q โค (only one โ (own copy)
โ handles it) producer โโถ topic โโผโโถ analytics service
โ (own copy)
competing consumers, โโโถ search indexer
load distribution (own copy)
fan-out, independent reactions
One "order placed" event can fan out to the email service, the analytics pipeline, and the search indexer at once, each reacting on its own. Subscriptions come in two flavors: ephemeral (you only get messages published while you're connected) and durable (the broker holds your messages until you come back). Durable subscriptions are what make pub/sub reliable across consumer restarts.
Event Streaming: The Log Model
an append-only log you can replayKafka and its kin are a different animal. Instead of deleting a message once consumed, the broker keeps an append-only log, partitioned for scale, retained for days or forever. Consumers track their own offset โ a bookmark into the log โ so they can replay history, reprocess after a bug, or add a brand-new consumer that reads from the beginning.
A topic is split into PARTITIONS (the unit of parallelism and ordering):
partition 0: [ m0 ][ m1 ][ m2 ][ m3 ][ m4 ]โโโโถ append
partition 1: [ m0 ][ m1 ][ m2 ]โโโโโโโโโโโโโโโโโถ append order guaranteed
partition 2: [ m0 ][ m1 ][ m2 ][ m3 ]โโโโโโโโโโโถ append WITHIN a partition
Consumer group (each partition โ exactly one consumer in the group):
consumer-A โโ reads partition 0 offset: at m3
consumer-B โโ reads partitions 1, 2 offsets tracked per partition
Parallelism is capped by partition count. More consumers than partitions
โ the extras sit idle.
Two consequences to remember. Ordering is guaranteed only within a partition, so anything that must stay ordered (all events for one user) has to hash to the same partition key. And partition count bounds parallelism: a consumer group can have at most one consumer per partition doing useful work, so you size partitions for your peak consumer count, not just your data volume (Sharding).
Delivery Guarantees
and why exactly-once is a trickThree levels, and the interviewer wants to hear that you know the middle one is the real default.
| Guarantee | Means | Failure mode |
|---|---|---|
| At-most-once | Fire and forget; ack before processing | Messages can be lost on a crash |
| At-least-once | Ack after processing; redeliver if unsure | Messages can be processed twice |
| Exactly-once | Every message takes effect once, no more | Expensive; often not what it seems |
Back-Pressure
when consumers fall behindA queue absorbs a spike, but an unbounded queue just hides a problem: if consumers are permanently slower than producers, the backlog grows without limit until memory or disk runs out. The buffer bought time, not a solution. Consumer lag โ how far behind the newest message the consumers are โ is the metric that tells you which is happening.
| Response | How it helps |
|---|---|
| Scale consumers | Add workers (up to the partition limit for logs) to drain faster |
| Bounded queues | Cap the buffer so the problem surfaces instead of hiding until OOM |
| Load shedding | Drop or sample low-priority messages under sustained overload |
| Producer throttling | Slow the producers so they can't outrun consumers indefinitely |
Quick Reference
queue vs pub/sub vs stream| Queue | Pub/Sub | Log stream | |
|---|---|---|---|
| Delivery | One consumer per message | Every subscriber gets a copy | Every consumer group, by offset |
| After consume | Message deleted | Delivered, then gone | Retained; replayable |
| Ordering | Best-effort | Best-effort | Guaranteed within a partition |
| Replay | No | No | Yes โ rewind the offset |
| Consumer model | Competing workers | Independent subscribers | Consumer groups |
| Canonical tool | SQS, RabbitMQ | SNS, Google Pub/Sub | Kafka, Pulsar, Kinesis |
| Best fit | Background jobs, task distribution | Event fan-out to many reactors | Event sourcing, CDC, stream processing |