Fanout is the "tell everyone" primitive: one event, many consumers, each with its own independent read position. The architecture that makes this safe is a single write point with per-consumer state. Trace one order.created event through the system.
trace stepSystem
The order service publishes order.created to the orders topic — one record, one partition append, one acknowledgment. Cost: one broker write (~1–5ms with acks=all in Kafka). The producer does not know or care how many consumers will read it. That asymmetry — one write, N reads — is the entire point of fanout.
trace stepSystem
The broker keeps the event on the topic's partition(s). Consumers subscribe: the email service, the analytics pipeline, the inventory service, the search indexer. In Kafka terms each is a consumer group; in RabbitMQ terms each binds a queue to the exchange. Routing is declarative and happens at the broker, not in the producer.
trace stepSystem
All four consumers read the same record independently — the analytics group reads offset 41 of partition 0 at the same moment the email group reads offset 41 of partition 0. They share the bytes on disk but never share progress. This is the key difference from a work queue: a queue splits messages among workers; a topic broadcasts them to groups.
trace stepSystem
Each group commits its own offset at its own pace. The email group commits after a fast 5ms process; the analytics group, batching for efficiency, commits every 10 seconds. Neither's position affects the other's. A group that rebalances (a consumer joins or leaves) resets to its own last committed offset and replays only its own unconsumed tail.
trace stepSystem
The search indexer's downstream API stalls; its group stops committing. Its lag grows: 100, 10,000, 100,000 records. Nothing else feels it — email and analytics keep committing, the broker keeps the lagging group's records for retention (Kafka default: 7 days). The slow consumer eventually recovers and catches up from its own offset, reprocessing only its own backlog.
trace stepSystem
This is the payoff: if the analytics consumer dies permanently, the system degrades one consumer, not the pipeline. The event was never deleted on consumption (that's the queue model); it remains on the topic until retention, so a repaired or replaced consumer resumes exactly where its group stopped. The only shared resources are broker disk and the topic's retention budget.
text
order.created ──▶ topic "orders" (partitioned, retained) ├── group: email → offset per partition, fast ├── group: analytics → offset per partition, batched ├── group: inventory → offset per partition └── group: search → offset per partition, lags, recovers
The ledger for a 10-consumer fanout of a 1,000 msg/s event stream: one producer write at ~1ms, ten consumer reads at ~1ms each, ten offset commits per interval, and broker disk for the 7-day retention window. The mechanism's guarantee — every consumer sees every event, at its own pace, without ever blocking the others — is what event-driven architectures are built on, and the isolation is the reason one broken consumer is an incident for one team, not for the platform.