RealWorld Examples and Case Studies
Real systems rarely pick one database; they route each access pattern to the store whose data model and consistency guarantees make that pattern cheap, then keep the copies in sync — the win comes from matching the shape of the query to the shape of the index, not from loyalty to SQL or NoSQL. This is polyglot persistence. The famous case studies below are only interesting once you see the mechanism underneath them, so we trace one end to end: how Facebook's TAO answers "who liked this post" for billions of users.
The mechanism: a graph-shaped cache over a relational store
Facebook's social data is a graph — users, posts, comments, likes — and the dominant query is a traversal: give me the edges around this node. A relational schema can store that, but every friend-list or like-count render becomes a join against a table with trillions of rows, and reads outnumber writes by roughly 500:1. TAO ("The Associations and Objects") is the layer that reconciles this: a graph-aware, read-through/write-through cache sitting in front of sharded MySQL, exposing exactly two primitives.
- Objects — typed nodes with a 64-bit id and a key/value blob, e.g.
user:308,post:42. - Associations — typed, directed, time-stamped edges between two ids, e.g.
(308, LIKES, 42). TAO also stores the inverse edge(42, LIKED_BY, 308)so the traversal works both ways.
The cache is two-tier and per-region: many follower tiers serve reads from RAM; one leader per region owns writes and cache fills for its shards; and each shard has a single master region that all writes for that shard must funnel through. MySQL is still the durable source of truth — TAO is the access pattern made fast, not a new database.
A worked trace: Alice (EU) likes post 42 (US-mastered)
Post 42 lives on shard 7, whose master region is US. Alice is served from EU. Here is the exact data and the two paths.
| Concept | TAO representation |
|---|---|
| Alice, the post | Objects user:308, post:42 |
| "Alice likes 42" | Association (308, LIKES, 42, t=1719900000) |
| Inverse, for the counter | Association (42, LIKED_BY, 308) |
| "how many likes?" | assoc_count(42, LIKED_BY) |
Read path — assoc_count(42, LIKED_BY) (99.8% of TAO ops are reads):
- EU app asks its local follower. Warm cache → returns
1,204,318in ~1 ms. Done for the vast majority of requests. - On a miss, the follower forwards to the EU leader.
- Leader miss → reads MySQL shard 7 (
id1=42, atype=LIKED_BY) from the EU replica, ~10–20 ms. - Leader caches the count and back-fills the follower; the next million reads are ~1 ms again.
Write path — assoc_add(308, LIKES, 42) (the rare 0.2%):
- EU leader sees shard 7 is US-mastered, so it forwards the write to the US master leader — a write cannot commit in EU.
- US master writes the edge to MySQL, creates the inverse
(42, LIKED_BY, 308), bumps the count to1,204,319. - MySQL replicates US→EU asynchronously; the master also invalidates/refills the follower tiers.
- Window of staleness: until replication + invalidation land, an EU follower may still answer
1,204,318. Alice sees her own like immediately only because TAO routes her read-after-write through the path that has the fresh value; a friend in EU may lag by tens of milliseconds. This is eventual consistency across regions, and it is a deliberate trade for read latency.
The other case studies, read as mechanism (not anecdotes)
- Netflix on Cassandra — write-heavy, multi-region, tunable. Viewing history and playback telemetry are append-heavy and multi-region, so they often choose leaderless replication with tunable consistency. The quorum-overlap condition
W + R > Nonly applies when both the write and the read actually reach their quorums: it gives stronger freshness (successful read/write sets must intersect), but it does not buy always-writable behavior during a partition. If a request usesQUORUM(typicallyW = R = ⌈(N+1)/2⌉), writes fail on any side of a partition that cannot reach a majority. To keep accepting writes in more places during a partition, the system lowers the write consistency level (e.g.LOCAL_ONEorONE), accepting thatW + Rmay no longer exceedN, so reads can be stale or conflicting and must be reconciled later. Last-write-wins (LWW) is a conflict-resolution rule (pick the higher timestamp when concurrent versions exist); it is not a strong-consistency guarantee. The cost you also accept: no cross-row transactions and no ad-hoc joins — you model tables per query in advance.
WithN=3, partition 2/1 Majority side (2 replicas) Minority side (1 replica) CL=QUORUMwrite (W=2)Succeeds Fails — only 1 replica reachable CL=ONEwrite (W=1)Succeeds Succeeds (both sides accept writes) CL=ONEread (R=1)May see either version May see either version; LWW later picks one by timestamp QUORUMreads and writes,W + R = 4 > N = 3, so successful quorums overlap — but the minority partition simply cannot form a write quorum. Lowering toONErestores write availability on both sides;W + R = 2 ≯ N, so freshness is no longer guaranteed by overlap. - Gaming: durable money in SQL, hot state in Redis. Accounts, purchases and inventory need ACID and audit, so they live in Postgres/MySQL. Live leaderboards and session state are read/written thousands of times per second and tolerate loss, so they live in Redis (sorted sets give O(log N) rank). Two stores because two access patterns: durable + transactional vs ephemeral + blisteringly fast.
- IoT (Philips Hue) on DynamoDB. Millions of device state updates keyed by device id — a pure key/value access pattern with predictable single-partition writes. DynamoDB's partitioned hash-key model matches it exactly; a relational schema would add join machinery no query here uses.
In every case the reasoning is the same sentence: this access pattern, at this scale, this read/write ratio → this store.
Pitfalls
- Dual writes with no source of truth. Writing the "same" fact to both SQL and a NoSQL/cache in application code means the two copies silently diverge when one write fails. Fix: pick one system of record and derive the rest via change-data-capture, an outbox, or write-through (as TAO does) — never two independent writes.
- Expecting cross-store transactions. You cannot get one ACID commit spanning MySQL and Redis/DynamoDB. Money moves in the transactional store; everything else reconciles asynchronously and must be idempotent and retry-safe.
- The hot-key thundering herd. When a celebrity post's cached count expires, a naive cache-aside lets a million concurrent misses stampede the database. TAO survives this by coalescing concurrent misses for the same id at the leader; a hand-rolled cache needs the same request-coalescing / single-flight logic or the DB falls over.
- Read-your-writes surprises across regions. The like you just made can appear "lost" to a reader in another region for tens of milliseconds. If a flow needs its own write back immediately, pin that read to the master path; don't assume the local replica is current.
- Polyglot as a dumping ground. "Every microservice picks its own database" multiplies backup, failover, on-call and schema-evolution surfaces. Each new engine is a permanent operational tax, not a free optimization.
When to reach for polyglot persistence — and when NOT to
Signals that point here: two or more access patterns whose data models genuinely conflict (transactional OLTP and graph traversal and full-text and time-series analytics); an extreme read:write skew or write volume a single node can't hold; a p99 or dollar cost on one pattern that a general-purpose store can't meet no matter how you index it.
The named alternative: one PostgreSQL. Modern Postgres absorbs many "NoSQL" patterns in-process — JSONB for schemaless docs, pg_trgm/GIN for search, arrays and recursive CTEs for shallow graphs, logical replication for read scale. What you gain by staying single-store: one transactional boundary, cross-domain joins, one backup/failover/monitoring surface, one mental model. What polyglot buys instead: each pattern runs on an engine built for it — at the cost of dual-write/CDC plumbing, eventual consistency between stores, and N operational surfaces to staff.
Decide like this: choose polyglot when a single store's data model actively fights the query at your scale — Facebook cannot traverse the friend graph on joins, Netflix cannot take global writes on a single leader. Prefer one Postgres until a specific, measured access pattern's latency or cost forces a specialized store; adopt the second engine for that pattern only, with a clear system of record. Splitting stores before you have that measurement buys complexity you'll pay for and speed you won't feel.
Takeaways
- Case studies are decisions, not trivia: each maps one access pattern + scale + read/write ratio to one store. Learn to recover that sentence for any system.
- TAO's trick is general — put a data-model-shaped, read-through cache in front of a durable relational store, and make writes funnel through a single master per shard for a coherent ordering.
- Polyglot persistence is an operational tax you pay for a real performance win; default to one capable store (often Postgres) and split only when a measured pattern forces it.
- Across stores you get eventual consistency, not transactions: designate a system of record, propagate with CDC/write-through, and make every consumer idempotent.
Re-authored and deepened for this guide. Primary source: Bronson et al., "TAO: Facebook's Distributed Data Store for the Social Graph," USENIX ATC 2013 (objects/associations model, two-tier leader/follower cache over sharded MySQL, ~99.8% read operations, per-shard master regions). Cassandra tunable-consistency and leaderless replication from the Apache Cassandra docs and Lakshman & Malik's original Cassandra paper; polyglot-persistence framing from Martin Fowler and from M. Kleppmann, "Designing Data-Intensive Applications" (O'Reilly, 2017). Latency and count values are illustrative but order-of-magnitude realistic.
🤖 Don't fully get this? Learn it with Claude
Stuck on RealWorld Examples and Case Studies? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.
Build the mental picture, not memorization.
I just read a lesson on **RealWorld Examples and Case Studies** (System Design) and want to truly understand it. Explain RealWorld Examples and Case Studies from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Socratic — adapts to where you're stuck.
Teach me **RealWorld Examples and Case Studies** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Active recall exposes what you missed.
Quiz me on **RealWorld Examples and Case Studies** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
Intuition + hook + flashcards for long-term memory.
Help me remember **RealWorld Examples and Case Studies** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.