Messaging

Decoupling producers from consumers: absorbing spikes, isolating failure, and smoothing load โ€” at the price of eventual consistency.

Queues Pub/sub Log streaming Delivery guarantees Back-pressure
๐Ÿงต

Why Async At All

A 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
What it buys the upside
  • 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.
What it costs the price
  • 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.
๐Ÿ’ก
Async is not free speed โ€” it's a trade. You move latency off the request path and pay with consistency and complexity (Tradeoffs). Use it when the work genuinely doesn't need to finish before you answer the user: sending email, transcoding, updating a search index, fanning out a feed.
๐Ÿ“ฅ

Message Queues

A 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.

Visibility timeout in-flight protection

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.

Dead-letter queue where poison goes

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.

โš ๏ธ
Visibility timeout plus retry means a message can be delivered more than once โ€” a slow consumer's timeout lapses while it's still working, so the message is handed to a second worker. That's the at-least-once reality, and it's why consumers must be idempotent (Concurrency Control).
๐Ÿ“ก

Pub/Sub

Where 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

Kafka 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).

๐Ÿ’ก
The replayable log is why streaming underpins event sourcing, CDC, and stream processing. The same log feeds a real-time consumer and a batch reprocessing job reading old offsets โ€” one source of truth, many readers at different positions (Batch & Stream, CDC).
๐ŸŽฏ

Delivery Guarantees

Three levels, and the interviewer wants to hear that you know the middle one is the real default.

GuaranteeMeansFailure mode
At-most-onceFire and forget; ack before processingMessages can be lost on a crash
At-least-onceAck after processing; redeliver if unsureMessages can be processed twice
Exactly-onceEvery message takes effect once, no moreExpensive; often not what it seems
โš ๏ธ
"Exactly-once delivery" over a network is essentially impossible โ€” the sender can never be sure an ack was lost or the message was, so it must choose to risk loss or risk duplication. What's achievable is an exactly-once effect: at-least-once delivery plus an idempotent consumer that ignores duplicates. When someone asks for exactly-once, the answer is "at-least-once with dedup" (Concurrency Control).
๐ŸŒŠ

Back-Pressure

A 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.

ResponseHow it helps
Scale consumersAdd workers (up to the partition limit for logs) to drain faster
Bounded queuesCap the buffer so the problem surfaces instead of hiding until OOM
Load sheddingDrop or sample low-priority messages under sustained overload
Producer throttlingSlow the producers so they can't outrun consumers indefinitely
โ„น๏ธ
Rising lag is an early-warning signal, not a full alarm โ€” the system still works, it's just falling behind. Alerting on lag catches a struggling consumer long before the backlog turns into dropped messages or a full disk (Resilience Patterns).
๐Ÿ“‹

Quick Reference

QueuePub/SubLog stream
DeliveryOne consumer per messageEvery subscriber gets a copyEvery consumer group, by offset
After consumeMessage deletedDelivered, then goneRetained; replayable
OrderingBest-effortBest-effortGuaranteed within a partition
ReplayNoNoYes โ€” rewind the offset
Consumer modelCompeting workersIndependent subscribersConsumer groups
Canonical toolSQS, RabbitMQSNS, Google Pub/SubKafka, Pulsar, Kinesis
Best fitBackground jobs, task distributionEvent fan-out to many reactorsEvent sourcing, CDC, stream processing
โ„น๏ธ
The one-liners to keep ready. Async trades latency-on-the-path for eventual consistency and complexity. Queue = one consumer per message and load distribution; pub/sub = fan-out to many; log = retained, replayable, ordered per partition. Delivery is at-least-once in practice, so make consumers idempotent โ€” that's how you get an exactly-once effect. Watch consumer lag; an unbounded queue only postpones the overload.