Module 5: Scaling the Database

1. Module introduction: when the single box no longer holds up

Description

By the end of this lesson you'll understand exactly which assumption breaks in this module and why it arrives right now. Throughout the previous four modules, Enlace lived comfortably inside a single box: a service that receives requests and, beside it, a database that stores the Link records. In module 1 you drew that box; in module 2 you put numbers on it —100 million new URLs a month, ~40 writes per second, ~4,000 reads per second, ~6 TB of data at five years—; in module 3 you modeled the record and generated the short_code; and in module 4 you put a cache in front so most reads didn't even touch the disk. All of that rested on a silent assumption: that a single machine is enough. This module is where that assumption stops being true, and where you learn what to do when the data no longer fits in one place and the reads are no longer served by a single server.

This matters because it's the leap that separates a toy system from a real system. A shortener running on your laptop with a thousand links needs none of this; Enlace, with 6 TB and 4,000 reads per second, has no alternative. And the leap isn't a switch you flip once: it's a set of decisions —how many machines, who accepts the writes, who serves the reads, how you split the data, what happens when you add one more machine— and each has a cost and a tradeoff. This module gives you the three tools to make those decisions with judgment: replication, sharding, and consistent hashing. They're not optional or interchangeable; each solves a different problem, and knowing which to use —and in what order— is a good part of what's evaluated in a system-design interview and, more important, of what keeps your system from going down on a Tuesday at three in the afternoon.

Connection to the module: this lesson is the map, not the territory. Here you won't shard anything yet; you'll understand the order of the seven lessons that follow and why they go that way. Lesson 2 defines the threshold: the three physical limits that force you to scale, and the two independent axes along which you scale. Lesson 3 introduces replication (primary/replica), the tool for the reads axis. Lesson 4 adds the honest asterisk: the replication lag, the price of reading from a replica. Lesson 5 crosses to the other axis with sharding and the choice of the shard key. Lesson 6 shows, by running code, why the naive way to shard —hash(key) % N— is a trap. And lesson 7, the heart of the module, runs consistent hashing and measures, side by side, how much better it distributes. Lesson 8 —the project— asks you to put it all together into a scaling plan for Enlace.

The bakery that only had one oven

Think of it this way. Imagine a neighborhood bakery with a single oven. At first it's perfect: it bakes the morning bread, the afternoon cookies, it's enough for all the neighborhood's customers. The owner doesn't need to think about anything but one oven.

One day the bakery becomes famous. Three things arrive at once, and they're three different problems, not one. First: so much product no longer fits in a single oven —you want to bake more variety and more volume than an oven physically holds—. Second: there's an enormous line of people who only come to buy already-made bread, and that line collapses the counter even though the oven has bread to spare. Third: when that single oven turns off for cleaning, the whole bakery closes, because there's no other.

Each problem calls for a different solution. For the line of buyers you don't need more ovens: you need more counters selling the same already-baked bread —that's replicating—. To bake more variety than fits in one oven you need to split the production: this oven makes the breads whose name starts with A–M, that other one the N–Z ones —that's sharding—. And to not close when an oven turns off you need redundancy, that a second oven can take over —that's failover, and it's a topic of the resilience guide, not this one—.

The lesson I want you to take from the bakery is this: "the bakery can't keep up" isn't a single problem, it's three, and confusing them is the most expensive mistake. Adding more counters doesn't let you bake more variety. Splitting the production among ovens doesn't shorten the buyers' line. This whole module is learning to look at Enlace and say, with numbers, which of the three problems you have and which tool it calls for.

The case: Enlace's numbers that force the decision

We don't start from scratch: we start from the anchor numbers you computed in module 2. And as throughout this guide, we don't quote them from memory —we reproduce them—. Here's the arithmetic that brings Enlace to this module's door:

# Enlace's anchor numbers, reproduced (not quoted)
writes_per_month = 100_000_000
seconds_per_month = 30 * 24 * 3600            # 2,592,000 s

qps_write = writes_per_month / seconds_per_month
qps_read = qps_write * 100                     # read:write ratio = 100:1

records_5y = 100_000_000 * 12 * 5              # 5 years of sign-ups
storage_bytes = records_5y * 1024              # ~1 KB per Link record

print(f"qps_write = {qps_write:.1f} writes/s")
print(f"qps_read  = {qps_read:,.0f} reads/s")
print(f"records at 5 years = {records_5y:,}")
print(f"storage = {storage_bytes / 1e12:.2f} TB")

What to expect. When you run it:

qps_write = 38.6 writes/s
qps_read  = 3,858 reads/s
records at 5 years = 6,000,000,000
storage = 6.14 TB

Rounding to the numbers we'll use throughout the module: ~40 writes/s, ~4,000 reads/s, 6 billion records, ~6 TB. Now look at each one through the eyes of "a single box":

  • ~40 writes/s is a modest number. A single database server digests it without sweating. This number, by itself, does not force you to scale the writes yet —remember it, because it's why replication arrives before sharding—.
  • ~4,000 reads/s is already serious. Even though the cache from module 4 absorbs 90% of the hot reads, the 10% that escapes —hundreds of reads per second against the disk— plus the spikes, plus the cold cache on startup, push a single node to its limit. This number forces you to spread the reads.
  • ~6 TB is the number that doesn't forgive. It's not a matter of speed: it's that 6 TB of data, plus its indexes, plus the working space, plus growth margin, is more than you want to have on a single node if you value being able to back it up, restore it, and operate it. This number forces you, sooner or later, to spread the data.

Three numbers, three different pressures, just like the bakery's three problems. The whole module is the ordered response to those pressures.

The two axes of scaling (the module's mental map)

Here's the idea that organizes everything. Scaling a database isn't a single direction: it's two independent axes, and each of this module's tools lives on one of them.

                 scale DATA and WRITES  (sharding)
                            ▲
                            │   shard 0     shard 1     shard 2
                            │  (A-I)        (J-R)       (S-Z)
                            │
   scale READS  ────────────┼───────────────────────────────►
   (replicas)               │
                            │  each shard can also have,
                            │  in addition, its own replicas
  • Horizontal axis — reads: here lives replication. You copy the database onto several identical machines; one accepts the writes (the primary) and the others serve reads (the replicas). You don't increase how much data fits or how many writes you handle: you increase how many reads you can serve in parallel. It's the direct answer to Enlace's 100:1.
  • Vertical axis — data and writes: here lives sharding. You split the data into pieces (shards) and each machine stores only its piece. Now you do increase how much data fits and how many writes you handle, because each shard receives only a fraction. The price is that the system becomes much more complex: you have to decide which data lives in which shard, and what happens when you add or remove a shard —that's where consistent hashing comes in—.

The two axes combine: a large system shards and replicates each shard. But they arrive in that order for an economic reason. Replication is cheaper and simpler, and it solves Enlace's most urgent problem (the reads). Sharding is more expensive and more complex, and you postpone it until you really have no other option (the data or the writes). This module respects that order: replicas first (lessons 3-4), sharding after (5-7).

The order of the seven lessons, and why it's that

LessonToolThe problem it solves, in one sentence
2The thresholdRecognizing, with numbers, when a single database no longer suffices
3ReplicationServing 4,000 reads/s by spreading them among several copies
4Replication lagThe price of reading from a copy: it may be a bit behind
5ShardingStoring 6 TB and spreading the writes when one machine isn't enough
6The mod-N problemWhy the naive way to spread breaks when changing nodes
7Consistent hashingSpreading so that adding a node moves ~K/N, not almost everything

We start by recognizing the threshold (lesson 2), because scaling too early is as serious a mistake as scaling too late: you add complexity you don't need. Then we attack Enlace's most urgent and cheapest problem, the reads, with replication (lesson 3), and we add its honest asterisk, the lag (lesson 4). Then we cross to the hard axis, the data, with sharding (lesson 5). And the last two lessons are a single story split in two: first I show you, by running code, why the obvious way to shard —hash(key) % N— is a trap that forces you to move almost all the data when you grow (lesson 6), and then how consistent hashing disarms that trap and moves only what's necessary (lesson 7). That contrast —two measured numbers, side by side— is the module's climax.

What this module does NOT touch

It's good to mark the boundary from now, because there are neighboring topics that seem to belong here and belong to another guide in the ecosystem.

Failover and resilience in depth are not this module's. Here you'll mention that a primary can go down and that another machine takes over, because it's impossible to talk about replicas without naming it. But how it's detected that the primary died, how the replacement is chosen without splitting the system in two ("split brain"), how an operation is safely retried (idempotency in depth, retry with backoff, circuit breaker) —all of that is the sibling guide resilience-and-reliability-patterns-guide. When in lesson 3 I say "if the primary goes down, a replica is promoted", that sentence is a door to that guide; I point it out to you, I don't cross it here.

Load balancing between application servers is module 6. In this module we spread data and database reads. Spreading the incoming requests among several copies of the Enlace service —round-robin, least-connections, health checks, stateless services— is module 6 of this same guide. They're close cousins (both "spread load"), but they operate at different layers: M6 spreads HTTP requests among app servers; M5 spreads data and queries among databases.

Consistency models in depth are module 7. In lesson 4 you'll run into eventual consistency —a replica that's behind— and you'll see why Enlace almost always doesn't care. But the complete framework —CAP, PACELC, strong vs. eventual consistency as a design decision— is module 7. Here you touch it in passing, with the concrete case; there you formalize it.

Common mistakes

Believing that "scaling the database" is a single thing (mental-model mistake). What happens: someone says "the database can't handle it" and jumps straight to sharding, or to adding replicas, without diagnosing which of the three limits they have. Why it happens: "scaling" sounds like a single lever, when there are several. How to detect it: if you can't say with a number which resource ran out —disk, read throughput, write throughput—, you haven't diagnosed yet, you just panicked. How to fix it: it's exactly what lesson 2 does, putting each symptom in front of its number and its tool.

Sharding before needing it (sequence mistake). What happens: a team shards from day one "to be prepared", and carries all the complexity —shard keys, remapping, cross-shard queries— when a single machine with a replica would have been enough for years. Why it happens: "thinking about scale" is confused with "building for scale" from the start. How to detect it: if your writes fit on one machine (Enlace's ~40/s fit with plenty to spare) and your data fits on one disk, sharding is premature complexity. How to fix it: the module's rule is replicas first (cheap, simple), sharding only when the data or the writes really don't fit. The order of the lessons is that rule made into a curriculum.

Confusing replicating with sharding (concept mistake). What happens: someone believes that adding replicas gives more storage capacity, or that sharding speeds up reads by itself. Why it happens: both "add machines", and it's easy to blur them. How to detect it: ask yourself "does each new machine have all the data or just a piece?". If it has all, it's a replica (scales reads, not storage); if it has a piece, it's a shard (scales storage and writes). How to fix it: the two-axes map from this lesson; a replica moves on the reads axis, a shard on the data axis.

Exercises

Exercise 1 — Diagnose the limit. For each Enlace symptom, say which of the three limits (storage, read throughput, write throughput) it is and which module tool it calls for. (a) The database server's disk is at 85% and growing ~100 GB a month. (b) At peak hour, the resolution queries (resolve) start taking 400 ms instead of 5 ms, even though the disk has plenty of space. (c) A director asks "what if tomorrow we launch a campaign and create a billion links in a week?".

See solution
  • (a) It's the storage limit. 6 TB projected and the disk filling up is a problem of how much data fits, not of speed. The tool is sharding (lesson 5): spread the data among several machines so none carries the 6 TB.
  • (b) It's the read-throughput limit. The reads get slow at peak but there's disk space: the bottleneck is how many reads per second a single node can handle. The tool is replication (lesson 3): copy the database and spread the ~4,000 reads/s among several replicas. Note that this arrives after the module 4 cache, not instead of it: cache first, replicas for what the cache doesn't absorb.
  • (c) It's the write-throughput limit (potential). A billion sign-ups in a week shoots the writes far above the normal ~40/s; a single machine that accepts all the writes (the primary) wouldn't handle it. The tool is sharding (lesson 5), which spreads the writes among several primaries, one per shard. Replicas do not help here, because all the writes still go to a single primary per shard.

Exercise 2 — Replica or shard. For each new machine added to a system, say whether it's a replica or a shard, and which axis it scales. (a) A machine that receives a complete and continuous copy of all the Link records and only answers read queries. (b) A machine that stores only the Links whose short_code falls in a certain range and accepts both reads and writes of that range. (c) A machine identical to the previous one but that only serves reads of that same range.

See solution
  • (a) It's a replica. It has all the data and only reads. It scales the reads axis: it adds read capacity without adding storage capacity (it stores the same as the others).
  • (b) It's a shard. It has a piece of the data (a range of short_code) and accepts writes. It scales the data and writes axis: among all the shards, more fits than would fit in one, and the writes are spread.
  • (c) It's a replica of a shard. It combines the two ideas: it's a read copy (replica) of a single shard (a piece). It scales the reads within that shard. That's what a mature system looks like: sharded on one axis and replicated on the other, which is exactly the topology you'll design in the project (lesson 8).

Exercise 3 — The order matters. A colleague proposes: "For Enlace, let's start by sharding into 8 machines from launch day, so we never have to migrate". With the anchor numbers in hand (~40 writes/s, ~4,000 reads/s, ~6 TB at five years), give two reasons why that order is a mistake and what the correct order would be.

See solution

Two reasons why sharding on day one is a mistake for Enlace:

  1. The writes don't justify it. ~40 writes/s fit with plenty to spare on a single machine; that's the only limit sharding solves and replicas don't. Sharding for 40 writes/s is carrying all the complexity of sharding (shard key, remapping when growing, cross-shard queries) with no benefit in return.
  2. The 6 TB are at five years, not at launch. On launch day the database is almost empty; the 6 TB accumulate over sixty months. Sharding into 8 machines from the start means eight almost-empty machines and eight times the operational complexity, for years, for a storage problem that doesn't yet exist.

The correct order: a single machine with a cache (what you already have after module 4); add read replicas when the ~4,000 reads/s start to squeeze (the real and near problem); and shard only when the storage or the writes really don't fit on one machine —and when that moment arrives, do it with consistent hashing (lesson 7) so you don't have to migrate almost everything each time you add a shard. That's exactly the order of this module's lessons, and it's no coincidence: it's the rule "scale late and on the right axis" made into a curriculum.

Summary and next step

In this lesson you understood the assumption this module breaks: that a single database is enough. With the single-oven bakery you saw that "I can't keep up" isn't one problem but three —less fits than you want to produce, the buyers' line collapses the counter, and there's no backup if the oven turns off— and that each calls for a different tool. You reproduced Enlace's anchor numbers and saw what pressure each one exerts: ~40 writes/s (still comfortable on one machine), ~4,000 reads/s (which force spreading reads), and ~6 TB (which force spreading data). And you met the module's mental map: two independent axes —replicas for the reads, sharding for the data and the writes— and the order in which they're attacked, replicas first for being cheap and simple, sharding after for being expensive and complex.

Before moving on you should be able to: name the three limits that push you to scale and give the Enlace number that triggers each; explain why a replica and a shard scale different axes; and justify why the correct order for Enlace is cache → replicas → sharding, and not the reverse.

What comes next is sharpening that diagnosis. In lesson 2 you'll put each of the three limits in front of its number and its concrete symptom, you'll see why vertical scaling —buying a bigger machine— has a ceiling and when you hit it, and you'll formalize the two axes we only drew here. It's the step from "I know a box isn't enough" to "I know exactly when it stops being enough and why".

Resources