System Design (High-Level Design) Module 19 – Building blocks: traffic, storage and messaging
Design a pub-sub system with fan-out
Design a publish-subscribe service with topics and subscriptions, push or pull delivery, ack deadlines, ordering keys, filters and dead letters.
What you will learn
- Estimate the deliveries, bandwidth and storage of a pub-sub service from its fan-out
- Design topics, subscriptions and message storage for many subscribers per topic
- Choose push or pull delivery and acknowledgement deadlines for each subscriber
- Order messages per key with ordering keys, filter at the service, and route poison messages to dead letters
- Plan for subscribers that fall behind, fail or disappear for days
Before you start
What you’ll learn · Before you start
What you will learn
- Estimate the deliveries, bandwidth and storage of a pub-sub service from its fan-out
- Design topics, subscriptions and message storage for many subscribers per topic
- Choose push or pull delivery and acknowledgement deadlines for each subscriber
- Order messages per key with ordering keys, filter at the service, and route poison messages to dead letters
- Plan for subscribers that fall behind, fail or disappear for days
Before you start
On this page
An “order placed” event on a company’s event platform must reach billing, email, search, analytics and 30 other services, and the busiest topic already has 300 subscriptions. A publish-subscribe: A messaging style in which a producer publishes an event and every subscriber receives its own copy and acts on it in its own time, without the producer knowing who listens. More in “Synchronous vs asynchronous communication” service lets the publisher send each message once and delivers a copy to every subscription, so the publisher never needs to know who is listening.
Each subscription tracks what its subscribers have acknowledged and delivers the rest again, by letting workers pull or by pushing to an endpoint. You will estimate what fan-out does to bandwidth and storage, step one event through a first design, and then work out where messages live, how long a subscriber may hold one, how ordering keys fence off a slow key, and what happens to a subscriber that disappears for days.
Fan-out turns 50 MB a second of publishing into 500 MB of delivery
Rates, fan-out, delivery and retention each change the design, so ask about them first. This platform’s answers:
| The question to ask | This platform’s answer |
|---|---|
| How many messages a second, and how large? | 50,000 at peak and 20,000 on average, 1 KB each |
| How many subscriptions per topic? | 10 on average, 300 on the busiest topic |
| Do subscribers pull, or must the service call their HTTPS endpoints? | Both |
| Must related messages arrive in order? | Yes, per key, such as per order |
| Delivered at least once, or more strictly? | At least once |
| How long may a subscriber be away, and may it replay old messages? | Messages are kept 7 days for replay |
Functional requirements: create topics and subscriptions; publish batches; pull, acknowledge and extend deadlines; push to endpoints with retries; ordering keys; filters; dead-letter topics; replay from a time. Non-functional requirements: no message lost once publish succeeds, delivery within about 100 ms for healthy subscribers, and a slow subscriber never slows the publisher or other subscribers.
The first estimate multiplies those answers:
# A first estimate for the pub-sub service of this lesson. The inputs are assumptions to replace with measurements.
PEAK_PUBLISH, AVG_PUBLISH = 50_000, 20_000 # messages published a second, across all topics
MSG_BYTES = 1_000
FAN_OUT = 10 # subscriptions per topic, on average
RETENTION_DAYS, COPIES = 7, 3 # kept for replay, and replicated three times
MB, TB = 10**6, 10**12
kept = AVG_PUBLISH * MSG_BYTES * RETENTION_DAYS * 86_400 * COPIES
print(f"publish at peak: {PEAK_PUBLISH:,} messages/s × {MSG_BYTES:,} bytes = {PEAK_PUBLISH * MSG_BYTES / MB:,.0f} MB/s in")
print(f"deliveries at peak with an average fan-out of {FAN_OUT}: {PEAK_PUBLISH * FAN_OUT:,} messages/s, "
f"{PEAK_PUBLISH * FAN_OUT * MSG_BYTES / MB:,.0f} MB/s out")
print(f"{RETENTION_DAYS} days kept for replay, {COPIES} copies: one shared log {kept / TB:,.1f} TB; "
f"a copy for every subscription {kept * FAN_OUT / TB:,.0f} TB") Output
publish at peak: 50,000 messages/s × 1,000 bytes = 50 MB/s in deliveries at peak with an average fan-out of 10: 500,000 messages/s, 500 MB/s out 7 days kept for replay, 3 copies: one shared log 36.3 TB; a copy for every subscription 363 TB
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 fanout_estimate.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Fan-out multiplies everything that happens after the publish: 50 MB a second in becomes 500,000 deliveries and 500 MB a second out. Storage depends on where messages live: 36.3 TB for one shared log, ten times as much if every subscription keeps its own copy.
Checkpoint
Check the estimate
The first design: one log per topic, a small state per subscription
Publishers write each message once to the topic’s log. Each subscription reads the log from its own cursor and hands messages out under a lease, pull and push dispatchers deliver them, and messages that fail too often go to a dead-letter topic.
The first design of the pub-sub service
Text description of the diagram
The diagram shows the first design from top to bottom.
- Publishers, the services that send events, call publish with a topic and a batch of messages.
- The publish API authenticates and validates the request, batches messages, and replies only once each message is stored.
- Topic storage keeps one partitioned, replicated log per topic, so each message is stored once however many subscriptions read it.
- Each subscription, such as billing or email, reads the log from its own cursor and keeps its leases: which messages are out to a subscriber and until when. A subscription may also have a filter or use ordering keys.
- Delivery happens in two ways: a pull API, where workers ask for messages and acknowledge them, and push dispatchers, which send each message to an endpoint with an HTTP POST and retry with backoff.
- A dashed arrow leads from delivery to a dead-letter topic, which receives messages after their last failed attempt.
Step one event through it, from the publish to both acknowledgements:
The drawing shows the whole scene; its 9 steps are listed below.
Every step as text
- Publish once. The order service publishes an "order placed" event to the orders topic. It sends the event once, whoever subscribes to it.
- Stored once. The publish API appends the event to the topic's log, which keeps it on several replicas. It is stored once, however many subscriptions read it.
- A reply after storing. Only once the event is stored does the API return its message ID. From then on, the service must not lose it.
- Every subscription sees it. Billing and email each have a subscription to the topic. Each reads the log from its own cursor, so the event is now waiting for both.
- Billing pulls. A billing worker asks for messages when it is ready and receives the event with a lease, an acknowledgement deadline after which the event would be delivered again.
- Billing acknowledges. The worker records the payment and acknowledges the event. The billing subscription will not deliver it again, and its cursor can move past it.
- Email is pushed. The email subscription sends the event to the email service's HTTPS endpoint. A success status in the reply is the acknowledgement.
- A failure stays apart. The endpoint answers with an error, so the email subscription keeps the event and retries later. Billing is not slowed, because each subscription has its own cursor and leases.
- Two deliveries. The endpoint answers 200 on the retry, and the event is done for both. One publish became two deliveries, a fan-out of 2; this platform averages 10, and its busiest topic has 300.
Six choices are still open, each in one sentence:
- A shared log with cursors: each message is stored once, and every subscription records how far it has read.
- A copy per subscription: each subscription keeps its own queue of the messages it has not acknowledged.
- Pull or push: workers ask for messages when they are ready, or the service sends each message to an endpoint.
- Acknowledgement deadlines: how long a delivered message may stay unacknowledged before it is delivered again.
- Ordering keys: messages with the same key reach a subscription in the order they were published.
- Filters and dead-letter topics: a filter passes only messages whose attributes match, and a dead-letter topic takes messages that failed too often.
The deep dives that decide whether the design works:
- Where are messages stored when one topic has 300 subscriptions, and what does each subscription keep?
- How long should an acknowledgement deadline be, and what happens when work takes longer?
- When should delivery be push, and when pull?
- How do ordering keys keep order without letting one slow key stall everything?
- Where do filters run, and what do they do to cost and backlog?
- What happens to a message that always fails, and to a subscriber that is away for two days?
An answer is judged on requirements and estimates, storage and fan-out, delivery and acknowledgement, ordering, failure handling and operations.
Checkpoint
Check the first design
A subscription is a cursor, the acknowledgements above it and a lease table Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- One shared log keeps storage flat as fan-out grows
- Delivery is a lease, and a deadline shorter than the work runs a message twice
- Pull lets the subscriber set the pace; push needs only an endpoint
- Ordering keys keep order per key and fence off a slow key
- Filters at the service spare every subscriber the unwanted messages
- A poison message needs an attempt limit, a dead-letter topic and a way back
- A subscriber away for two days loses time, not messages
- Two days away
- An endpoint that fails for an hour
- A storage node that dies
- At 10×, delivery is the bottleneck; at 100×, the number of subscriptions is
- Alternatives rejected
- What each level is expected to cover
- A sample answer and a 45-minute self-mock
- Practise: dispatch in order per key
- Dispatch messages in order per key, in parallel across keys
- Interview questions
- One topic receives 20,000 messages a second of 1 KB and has 300 subscriptions, and each subscription must be able to replay the last 7 days. Your first design keeps a queue per subscription. Estimate its storage and write load, and propose a better design with its trade-offs.
- A billing subscriber pulls "order paid" events with a 10-second acknowledgement deadline. On busy days some customers are charged twice, and the duplicates line up with charges that took more than 10 seconds. Explain the mechanism and fix it on both the subscription and the subscriber side.
- A subscription uses ordering keys set to the merchant ID. One large merchant's events now arrive minutes late, and once a malformed event stopped that merchant's events for an hour. Every other merchant is fine. Explain both problems and redesign the keys and the failure handling.
- Key takeaways
3 diagrams · 3 runnable examples (JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 61 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- Pub/Sub subscription overview (Google Cloud)
- Pub/Sub subscription properties (Google Cloud)
- Pub/Sub push subscriptions (Google Cloud)
- Pub/Sub, extend ack time with lease management (Google Cloud)
- Pub/Sub, order messages (Google Cloud)
- Pub/Sub, filter messages from a subscription (Google Cloud)
- Pub/Sub, dead-letter topics (Google Cloud)
- Apache Pulsar documentation, Messaging concepts (Apache Software Foundation)
- NATS, publish-subscribe (NATS (Synadia and the NATS authors))
- Publisher-Subscriber pattern (Azure Architecture Center) (Microsoft)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress