Section 6: A Read Machine With a Small Write Engine Attached - High-Level Architecture
Executive Summary: The architecture follows Section 3's arithmetic. Reads arrive at 10,500 page views a second and writes at 400, so the design is a large read machine (CDN, stateless render tier, bundle cache, read replicas) with a small write engine attached (one content service, sharded Postgres, a transactional outbox). Purges, search indexing, reputation, feed ranking, notifications and spam ML all run asynchronously from the outbox. We follow a reader in Lagos through the read path and Daniel's answer through the write path, then show how it degrades: pages the CDN holds stay up when the origin is down, and writes fail closed.
The interviewer hands over the marker: "You have the API and the data model. Draw the boxes."
You: "I'll draw it in proportion to the load. 85% of page views never leave the CDN, and all writes together peak at about 400 a second. So most boxes serve reads cheaply, the write side is one service and one transaction per action, and everything else hangs off an event stream."
The Components
| Component | Job | Load at peak (Section 3) | Deep dive |
|---|---|---|---|
| CDN + origin shield | Serves the same HTML to everyone; s-maxage=60, SWR 60, stale-if-error=86400; also serves images |
10,500 views/s, 85% hits, ~5 Gbps egress | Section 7 |
| Object storage | Image uploads, never in the DB | 4 GB/day | — |
| API gateway | Session auth, token-bucket rate limits by reputation tier and IP | All origin traffic | Section 11 |
| Render tier | Stateless HTML rendering on a CDN miss | ~1,600 renders/s, ~0.76 Gbps | Section 7 |
| Content service | Owns posts, comments, votes and revisions; the only writer to the content clusters | ~400 writes/s, 2,100 viewer-state calls/s | Section 8 |
| Bundle cache | bundle:{question_id}, content only, no scores |
95% hits, 150 GB working set | Section 7 |
| Postgres | 2 clusters, each primary + sync standby + 2 async replicas | ~4,000 queries/s across replicas | Section 5 |
| Outbox → CDC → Kafka | Every committed change becomes an event | ~134 events/s avg | This section |
| Consumers | Purge, index, reputation, hot rank, notify, spam ML | Notifications ~46/s | Sections 8, 9, 11 |
| Spam fast path | Rules-based allow / hold / reject on every post | ≤ 50 ms per call | Section 11 |
Interviewer: "Why one content service? I'd expect a question service, an answer service, a vote service."
You: "Because Section 5 put a question, its answers, comments and votes on one shard so that a vote is a single-shard transaction: the vote row, the score and the outbox row commit together. Split those across services and that one local transaction becomes a distributed one. At 400 writes a second nothing justifies that cost. The boundary I keep is between this service and everything downstream: the event stream. When a cluster passes about 3 TB, logical shards move to a new cluster. That's a data move, and the service boundary stays put."
Quick check before moving on:
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson