One New Server Shouldn't Take Down Your Database
It's a trick for sharing toys between friends without any fighting — and the same trick keeps Cassandra, DynamoDB, and half the internet's caches from falling over.
We'll learn each idea twice: first as a story a five-year-old gets, then as the real database version.
1. Where does each toy go?
🧸 Like you're five: A pile of toys, a few boxes. You need a rule so tomorrow you can find your teddy on the first try — same toy always in the same box, and no box stuffed while others sit empty.
Real version: keys → servers. The rule must be deterministic (same key → same server) and balanced (load spread evenly).
2. The easy way that quietly breaks
🧸 Line up 4 boxes. Count as you drop toys in: "1, 2, 3, 4, 1, 2, 3, 4…" Now add a 5th box. The count shifts — and almost every toy belongs somewhere new. Dump the pile, start over. Meltdown.
Real version: that counting rule is modulo hashing —
const getServer = (key, numServers) => hash(key) % numServers;
The number of servers is baked into the math. Change it and the divisor changes, so nearly every key remaps at once. If those servers are caches, that's a wall of cache misses stampeding your database — the classic "cache stampede" that takes systems down right when you add capacity.
3. The clever way: stand in a circle
🧸 Instead of a line of boxes, your friends hold hands in a circle. Drop a toy anywhere — it rolls forward until it bumps into a friend. That friend keeps it.
Real version: the circle is a hash ring (positions 0 → 2³², wrapping around). Hash each server's name to place it on the ring. Hash each key to place it too. Then walk clockwise to the first server — that's the owner.
4. Why the circle wins
🧸 A friend leaves for the bathroom → only their toys roll to the next friend. A new friend squeezes in → they grab only the toys in the little gap in front of them. Everyone else is untouched.
Real version: add or remove a server and only its slice of the ring is affected. Here's the whole difference in one look:
| When the fleet changes | Modulo hashing | Consistent hashing |
|---|---|---|
| Add / remove a server | ~all keys remap 💥 | ~one server's share moves ✅ |
| What that causes | cache stampede, DB overload | smooth, localized shuffle |
That single row is the entire reason consistent hashing exists.
5. The catch: greedy friends
🧸 If friends bunch up on one side, the friend with a big empty space in front grabs way too many toys. And if that friend leaves, the next one gets buried all at once.
Real version: servers hashed to random spots aren't evenly spaced, so some own huge arcs (hotspots) and a dying server dumps its whole load on one neighbor — which can then topple too.
6. The fix: many name tags
🧸 Give each friend a fistful of name tags and scatter them all around the circle. Now everyone catches toys from lots of little spots, so it's fair — and if someone leaves, their toys scatter to many friends, not one.
Real version: those scattered tags are virtual nodes — place each server at many points (Server#1 … Server#100). The full thing in JS is surprisingly small:
// tiny 32-bit hash — good enough to demo the idea
const hash = (str) =>
[...str].reduce((h, c) => (h * 31 + c.charCodeAt(0)) >>> 0, 0);
class HashRing {
constructor(servers = [], vnodes = 100) {
this.vnodes = vnodes;
this.ring = new Map(); // ring point -> server
this.points = []; // sorted ring points
servers.forEach((s) => this.add(s));
}
add(server) {
for (let i = 0; i < this.vnodes; i++) {
const p = hash(`${server}#${i}`);
this.ring.set(p, server);
this.points.push(p);
}
this.points.sort((a, b) => a - b);
}
get(key) {
const p = hash(key);
// first ring point clockwise from the key, else wrap to the start
const hit = this.points.find((x) => x >= p) ?? this.points[0];
return this.ring.get(hit);
}
}
(In production you'd binary-search points instead of .find(), but the idea is exactly this.)
7. Where the grown-ups use it
DynamoDB, Apache Cassandra, Memcached sharding, CDNs picking an edge cache, load balancers with sticky routing — anywhere data is spread across a fleet that grows and shrinks.
8. The one thing to remember
Modulo remaps everything. The ring remaps one server's worth. Virtual nodes keep it fair. That's consistent hashing.
If this made a hard idea click, I write about backend and system-design fundamentals in plain language. Follow along.





Top comments (0)