Skip to content

Messaging

Messaging components put an asynchronous boundary between the part of the system that produces work and the part that does it. Instead of calling a service and waiting, a producer writes a message to a broker and moves on; consumers read it when they can. That single change decouples the two sides in time (the consumer may be down or slow), in rate (a spike becomes a backlog that drains at the consumers' pace) and in knowledge (the producer does not need to know who consumes). The price: the result is no longer immediate, messages can arrive twice or out of order, and you now have to watch backlogs and lag.

In BoxOffice, the waiting room of the flash sale is a queue; order publishes OrderConfirmed events to Kafka, and notification, analytics and search each consume them independently; e-mail sending sits behind a queue so a slow provider never slows checkout.

Component One-liner Playground id
Message Queue Each message is processed by one of the competing consumers; work distribution queue
Pub/Sub Each message is delivered to every subscriber; fan-out of notifications pubsub
Event Stream An ordered, retained, partitioned log that consumers read at their own position stream
Kafka The most widely used event-streaming platform (topics, partitions, consumer groups) kafka
flowchart LR
    P1[order] -->|OrderConfirmed| K[(Kafka topic)]
    K --> N[notification]
    K --> A[analytics]
    K --> S[search]
    W[gateway] -->|admission tickets| Q[[Queue: waiting room]]
    Q --> I1[inventory worker]
    Q --> I2[inventory worker]
Queue Pub/Sub Event stream / Kafka
Who gets a message one consumer every subscriber every consumer group (one consumer per partition inside a group)
After consumption deleted (acked) deleted per subscription kept until retention expires; can be replayed
Ordering usually none (FIFO variants exist) usually none per partition (per key)
Typical use background jobs, buffering spikes notifications, cache invalidation, fan-out event-driven architecture, CDC, analytics pipelines

Message Queue

What it is. A message queue stores messages until a consumer takes them. Several consumers can read from the same queue β€” competing consumers β€” and each message goes to only one of them, so adding consumers increases throughput. A consumer acknowledges a message after processing it; if it crashes before the ack, the message becomes visible again (after a visibility timeout) and another consumer retries it. Messages that keep failing are moved to a dead-letter queue (DLQ) for inspection.

When to use.

  • Background work the user does not need to wait for: sending e-mails, generating PDF tickets, transcoding videos, resizing posters.
  • Absorbing spikes: producers enqueue at 10Γ— the rate for a minute; consumers drain at a steady rate β€” the backlog is the buffer (scenario async-queue).
  • Load leveling in front of a fragile resource: BoxOffice's waiting room admits fans into inventory at the rate the database can sustain.
  • Retrying calls to unreliable external providers without blocking the request path.

When not to (or watch out).

  • When the caller needs the result now (show the seat as held): keep that path synchronous.
  • When several independent services need the same message: use pub/sub or a stream, not one queue per consumer glued by hand.
  • Delivery is usually at-least-once: a message may be processed twice β†’ consumers must be idempotent.
  • Ordering is usually not guaranteed (or limited to FIFO queues with lower throughput).
  • A growing backlog is a silent failure: monitor queue depth and the age of the oldest message, and alert on them.

Key numbers.

Metric Typical value Note
Enqueue latency (managed queue) β‰ˆ a few to tens of ms Producer gets an ack and continues
Drain time of a backlog backlog Γ· (consumer rate βˆ’ arrival rate) 120 k messages, consumers 2 k/s faster than arrivals β‡’ 60 s
SQS visibility timeout default 30 s, max 12 h Must exceed the processing time
SQS retention default 4 days, max 14 days Older messages are dropped
Message size small β€” KBs, up to the broker's limit Large payloads by reference (claim check: store in object store, send the key)

Failure modes.

  • Poison message β€” one message always crashes the consumer and is retried forever β†’ max receive count, then DLQ.
  • Duplicate processing β€” consumer crashed after doing the work but before the ack β†’ idempotent consumer (dedupe by message id).
  • Unbounded backlog β€” consumers slower than producers for a long time β†’ autoscale consumers on queue depth, backpressure, shed or reject at the edge.
  • Visibility timeout too short β€” slow messages reappear and are processed in parallel by two consumers β†’ timeout longer than p99 processing time, heartbeat extension.
  • Consumer overload of the downstream β€” scaling consumers floods the database β†’ cap consumer concurrency.

In the playground. queue is a messaging node and therefore an async boundary: the producer gets an ack after the broker's few milliseconds, the message enters the backlog, and downstream consumers (e.g. worker) drain it at their capacity, each message going to one consumer. Watch the metrics backlog, consumer lag (s) and end-to-end (async) latency; when the backlog exceeds its maximum or retention, messages are dropped and counted as errors. Scale the consumers' replicas or autoscale to drain faster. Scenarios: async-queue, flash-sale, notification-system, video-streaming.

Real products.

  • Amazon SQS β€” fully managed queues: Standard (at-least-once, best-effort order) and FIFO (ordered, exactly-once processing within a deduplication window).
  • Amazon MQ β€” managed RabbitMQ and ActiveMQ.
  • Cloud Tasks β€” managed queue of HTTP tasks with rate limits and retries.
  • Pub/Sub with pull subscriptions β€” competing consumers on one subscription.
  • Azure Service Bus (queues) β€” enterprise messaging with sessions, DLQ and transactions.
  • Azure Queue Storage β€” simple, cheap queues.
  • RabbitMQ β€” the classic message broker (AMQP), queues and exchanges.
  • Apache ActiveMQ β€” JMS message broker.
  • Redis Streams / libraries such as Celery (Python) and BullMQ (Node.js) β€” job queues on top of Redis or RabbitMQ.

In the interview

"Anything the user doesn't need to wait for goes to a queue: checkout responds immediately and workers send the e-mail and generate the ticket. The queue absorbs spikes, retries failures and parks poison messages in a DLQ; because delivery is at-least-once, the consumers are idempotent, and I alert on queue depth and the age of the oldest message."

Learn more: Messaging β€Ί Queues & pub/sub Β· Messaging β€Ί Delivery semantics Β· Compute β€Ί Async work Β· Case study: notification system Β· Hands-on 7


Pub/Sub

What it is. In publish/subscribe, a producer publishes a message to a topic and the broker delivers a copy to every subscription. Publishers don't know who listens; adding a new subscriber requires no change to the publisher. Each subscription behaves like its own queue (with acks and retries). Classic pub/sub systems delete a message once each subscription has acknowledged it, and are optimized for low-latency fan-out rather than long retention or replay.

When to use.

  • Fan-out of events to several independent services: EventCancelled β†’ refunds, notifications, search index, cache invalidation.
  • Real-time delivery across servers: in a chat, the server that receives a message publishes it on the conversation's channel and the servers holding the recipients' WebSockets deliver it (scenario chat).
  • Cache invalidation across many app replicas.
  • Notifications to many endpoints (mobile push, e-mail, webhooks) through per-channel subscriptions.

When not to (or watch out).

  • When you need to replay history or add a consumer that must read past events: use an event stream (Kafka) with retention.
  • When only one worker should process each message: that is a queue (or one subscription with competing consumers).
  • Ordering across messages is usually not guaranteed; per-key ordering may be an option with lower throughput.
  • In-memory pub/sub (Redis Pub/Sub) is fire-and-forget: subscribers that are offline lose the messages.
  • Fan-out multiplies load: one publish Γ— N subscribers Γ— their downstream calls.

Key numbers.

Metric Typical value Note
Publish-to-deliver latency β‰ˆ tens of ms (managed services) β‰ˆ illustrative; in-memory brokers β‰ˆ sub-ms to a few ms
Fan-out cost one publish β†’ N deliveries N subscriptions Γ— message rate = delivery rate
GCP Pub/Sub message retention default 7 days on subscriptions (configurable) Unacked messages are redelivered
Max message size from β‰ˆ 256 KB (SNS) to 10 MB (GCP Pub/Sub) Documented limits; large payloads by reference
Delivery semantics at-least-once (typical) Subscribers must be idempotent

Failure modes.

  • Slow subscriber β€” one subscription accumulates a backlog while the others are fine β†’ per-subscription monitoring, scale that consumer, DLQ.
  • Lost messages with fire-and-forget pub/sub when a subscriber is down β†’ durable subscriptions or a stream.
  • Duplicates on redelivery β†’ idempotent subscribers.
  • Fan-out storm β€” a popular topic triggers thousands of downstream calls at once β†’ batching, rate limits on subscribers.
  • Accidental coupling β€” subscribers depend on undocumented fields of the message β†’ schemas and versioning.

In the playground. pubsub is a messaging node (async boundary) that delivers a copy of every message to each consumer connected to it (fan-out), unlike queue, where consumers compete. Each consumer has its own backlog and lag. Watch how adding a consumer multiplies the total work but does not slow the producer. Scenarios: chat, event-fanout, notification-system.

Real products.

  • Amazon SNS β€” managed pub/sub topics; often fanned out to SQS queues (SNS + SQS).
  • Amazon EventBridge β€” event bus with content-based routing rules.
  • Pub/Sub β€” global, managed publish/subscribe with push and pull subscriptions.
  • Eventarc β€” event routing to Cloud Run and other services.
  • Azure Service Bus (topics and subscriptions) β€” durable pub/sub with filters.
  • Azure Event Grid β€” event routing with push delivery.
  • Azure Web PubSub β€” pub/sub over WebSockets to clients.
  • NATS β€” lightweight, very fast messaging (core pub/sub; JetStream adds persistence).
  • RabbitMQ β€” fan-out and topic exchanges.
  • Redis Pub/Sub β€” in-memory, fire-and-forget channels.

In the interview

"When an event is cancelled, the event service just publishes EventCancelled; refunds, notifications and search each have their own subscription and react independently. Adding a consumer doesn't touch the publisher. Delivery is at-least-once, so every subscriber is idempotent, and I monitor each subscription's backlog separately."

Learn more: Messaging β€Ί Queues & pub/sub Β· Messaging β€Ί Patterns Β· Edge β€Ί Protocols Β· Case study: chat Β· Case study: news feed


Event Stream

What it is. An event stream is an append-only, ordered log split into partitions and retained for a period (hours, days or forever), not deleted on consumption. Each consumer (or consumer group) keeps its own offset β€” its position in the log β€” so many independent consumers can read the same events at their own pace, and a new consumer can replay history from the beginning. Events with the same key go to the same partition, which gives ordering per key. This is the backbone of event-driven architectures, change data capture (CDC) and real-time analytics. Kafka is the best-known implementation and has its own entry below; this entry covers the concept and the managed alternatives.

When to use.

  • Many consumers of the same facts, each at its own pace: OrderConfirmed read by notification (now), analytics (in batches) and a new fraud service (replaying last month).
  • Event sourcing and CQRS: the log is the source of truth and read models are rebuilt from it.
  • CDC: every change of the orders table published as an event (e.g. Debezium) to feed search, cache invalidation and the warehouse.
  • High-throughput ingestion: clicks, location updates, metrics, logs.

When not to (or watch out).

  • Simple background jobs with one consumer type: a queue is simpler (per-message acks, DLQ built in).
  • Request/response interactions: a stream adds latency and complexity for no gain.
  • Parallelism is bounded by the number of partitions; ordering only holds within a partition β€” pick the key carefully (e.g. order_id).
  • A slow consumer does not slow the others, but its lag grows; if lag exceeds retention, it loses events.
  • Schemas evolve: use a schema registry and compatible changes, or old consumers break.

Key numbers.

Metric Typical value Note
Kinesis shard capacity 1 MB/s or 1,000 records/s in; 2 MB/s out Documented per-shard limits
Kinesis default retention 24 h (extendable up to 365 days) Documented
Max consumer parallelism (per group) = number of partitions/shards Extra consumers sit idle
Consumer lag messages (or seconds) behind the head of the log The key health metric
End-to-end latency β‰ˆ tens to hundreds of ms β‰ˆ illustrative; depends on batching and polling

Failure modes.

  • Consumer lag growing β€” consumers slower than producers β†’ more consumers (up to the partition count), more partitions, batching, faster handlers.
  • Hot partition β€” one key (the flash-sale event) gets most events β†’ a finer key (seat_id instead of event_id), or accept the limit.
  • Lag beyond retention β€” events lost for that consumer β†’ alert on lag well before retention, longer retention.
  • Reprocessing side effects on replay (e-mails sent again) β†’ idempotent consumers, separate replay pipelines.
  • Breaking schema change β†’ schema registry with compatibility checks.

In the playground. stream is a messaging node with fan-out (every consumer gets every event) and a partitions parameter (default 4): the maximum parallelism of each consumer is min(partitions, consumer replicas). Adding consumer replicas beyond the partition count does not reduce lag β€” try it. Metrics: backlog, consumer lag (s), end-to-end latency. Scenarios: event-fanout, ride-sharing, news-feed.

Real products.

  • Amazon Kinesis Data Streams β€” managed, sharded event streams.
  • Amazon MSK β€” managed Apache Kafka (see Kafka).
  • Pub/Sub with ordering keys and seek/replay β€” a managed stream-like service.
  • Managed Service for Apache Kafka β€” managed Kafka clusters.
  • Azure Event Hubs β€” partitioned event ingestion service, with a Kafka-compatible endpoint.
  • Apache Kafka β€” the reference event-streaming platform.
  • Apache Pulsar β€” streaming and queuing with tiered storage.
  • NATS JetStream β€” persistent streams on NATS.
  • Redis Streams β€” lightweight streams with consumer groups.

In the interview

"Order events go to a partitioned, retained log keyed by order_id: each consumer group reads at its own offset, so notification, analytics and a future fraud service are independent and can replay history. Ordering is per key, parallelism is capped by the partition count, and the number I watch is consumer lag."

Learn more: Messaging β€Ί Queues & pub/sub Β· Messaging β€Ί Kafka Β· Messaging β€Ί Patterns Β· Case study: ride sharing


Kafka

What it is. Apache Kafka is a distributed event-streaming platform. Data is organized in topics, each split into partitions; each partition is an ordered, append-only log replicated across brokers (one leader, followers in the in-sync replica set). Producers choose the partition by key (same key β†’ same partition β†’ ordered). Consumers join consumer groups: within a group each partition is assigned to one consumer, and the group commits offsets to remember where it stopped. Messages are retained by time or size (or kept forever as the latest value per key with log compaction). Since Kafka 4.0, clusters run in KRaft mode (Raft-based metadata quorum, no ZooKeeper). Around the core: Kafka Connect (integrations, CDC with Debezium), Kafka Streams (stream processing) and schema registries.

When to use.

  • The event backbone of a microservice system: BoxOffice order, payment and inventory publish domain events; others react (choreographed sagas, read models).
  • Transactional outbox + CDC: Debezium reads the outbox table and publishes to Kafka, so database and events never diverge.
  • High-volume pipelines: clickstreams, logs, metrics, location updates β€” with replay for reprocessing.
  • When several teams need the same events with independent pace and retention.

When not to (or watch out).

  • Small systems with a few background jobs: a managed queue is far simpler to operate.
  • Per-message routing, priorities, delayed messages and per-message retries are not Kafka's strengths (queues do them better).
  • Partition count is a capacity decision made up front: it caps consumer parallelism, and adding partitions later changes the key β†’ partition mapping (ordering per key breaks across the change).
  • "Exactly-once" exists for Kafka-to-Kafka processing (idempotent producer + transactions); side effects outside Kafka still need idempotent consumers.
  • Operating Kafka (brokers, storage, rebalancing, upgrades) is real work β†’ consider a managed service.

Key numbers.

Metric Typical value Note
Default retention 7 days (log.retention.hours = 168) Configurable per topic; compaction keeps last value per key
Default max message size β‰ˆ 1 MB Large payloads by reference
Replication factor 3 with min.insync.replicas = 2 and acks=all Tolerates one broker failure without losing acknowledged writes
Produce latency (acks=all, same region) β‰ˆ a few ms to tens of ms β‰ˆ illustrative; batching (linger.ms) trades latency for throughput
Throughput per partition β‰ˆ tens of MB/s β‰ˆ illustrative; plan partitions = target throughput Γ· per-consumer throughput
Consumer parallelism per group ≀ partitions 6 partitions, 10 consumers β‡’ 4 idle

Failure modes.

  • Consumer lag β€” consumers can't keep up β†’ scale consumers up to the partition count, add partitions (planned), optimize the handler, batch.
  • Rebalance storms β€” consumers joining/leaving repeatedly pause consumption β†’ cooperative rebalancing, stable group membership, longer session timeouts.
  • Broker failure β†’ leader election for its partitions from the in-sync replicas; with RF 3 and min.insync.replicas = 2, writes continue.
  • Poison message blocks a partition (consumer keeps failing on the same offset) β†’ retry topic + DLQ topic, skip after N attempts.
  • Duplicates after a consumer crash between processing and offset commit β†’ idempotent consumer (dedupe table keyed by event id).
  • Hot partition from a skewed key β†’ better key choice, or split hot keys.
  • Disk full from retention too long or a stuck compaction β†’ monitor broker disk, tiered storage.

In the playground. kafka is a messaging node like stream (fan-out to every consumer, retained backlog) with an explicit partitions parameter (default 12, on 3 brokers): each consumer's parallelism is min(partitions, consumer replicas). Try the classic mistake: 3 partitions, scale the consumer to 10 replicas, and watch the lag not move. Metrics: backlog, consumer lag (s), end-to-end latency; retention limits turn excess backlog into drops (errors). Scenarios: event-fanout, payment-system, flash-sale, observability-stack.

Real products.

  • Amazon MSK (Managed Streaming for Apache Kafka) β€” provisioned or serverless Kafka clusters; MSK Connect for connectors.
  • Managed Service for Apache Kafka β€” managed Kafka clusters on Google Cloud.
  • Azure Event Hubs β€” exposes a Kafka-compatible endpoint, so Kafka clients work without running brokers.
  • Confluent Cloud (available through cloud marketplaces) β€” fully managed Kafka from Kafka's original creators.
  • Apache Kafka β€” brokers in KRaft mode (used in Hands-on 7).
  • Strimzi β€” Kubernetes operator to run Kafka.
  • Debezium β€” CDC connectors that turn database changes (or an outbox table) into Kafka events.
  • Kafka Connect and Kafka Streams β€” integration and stream processing, part of the Apache Kafka project.

In the interview

"I'd use Kafka as the event backbone: the order service writes to an outbox in the same transaction, Debezium publishes to a topic keyed by order_id, and notification, analytics and search are separate consumer groups. I size partitions for the consumer parallelism I need, run replication factor 3 with acks=all, and make every consumer idempotent because delivery is at-least-once."

Learn more: Messaging β€Ί Kafka Β· Messaging β€Ί Delivery semantics Β· Messaging β€Ί Patterns Β· Distributed β€Ί Consensus Β· Case study: payment system Β· Hands-on 7