Every Tuesday - Deep dives, architecture lessons, and real engineering stories.

Every Saturday - The best DevOps, SRE, Cloud, AI, tools, tutorials, and projects from the week.

📬 Missed This Week’s Uptime Sync?

This week’s best DevOps, SRE, Cloud, Kubernetes, database, AI infrastructure, and production engineering reads:

  • Why Basecamp left the cloud

  • How Airbnb captures and replays real database workloads

  • What AI found in MariaDB that humans missed

  • PostgreSQL row-level security performance in practice

  • Why one team moved off Firestore

  • How a custom Ubuntu mirror made package installs faster

  • What Kubernetes’ shift to cgroup v2 means for you

Plus: Docker layer trade-offs, DynamoDB warm throughput, SSO-backed kubectl access, self-hosted HTTP tunnels, HAProxy monitoring, and nginx password protection.

You have four cache nodes, and everything is working fine. Then traffic grows, you add a fifth node, and suddenly a huge chunk of your keys point to different servers.

That can mean cache misses everywhere, more traffic hitting your database, and large amounts of data moving between nodes, which can overload disks and networks. If the system handles it badly, you have an incident.

Consistent hashing is designed to shrink that problem. To see how, start with the way most systems decide which server owns a key.

Modulo hashing

Say you have four cache servers, Node 0 through Node 3, and you need to store user_12345. The system needs a repeatable way to pick the owner. A very common approach is:

owner = hash(key) % number_of_nodes

The hash function turns the key into a number, and the remainder picks the node:

hash("user_12345") --> 78291
78291 % 4 = 3      --> Node 3

As long as the node count stays the same, the same key always lands on the same server. It's simple and fast, and it works well until the cluster size changes.

What happens when you add a server

Adding one node in modulo-based sharding can remap most keys.

Say one node is getting full, so you add a fifth. The rule goes from hash(key) % 4 to hash(key) % 5.

It looks like a tiny change, but changing the divisor changes the result for most keys. Take 100 keys numbered 0 to 99 and, to keep it simple, treat each key's number as its hash. Compare key % 4 with key % 5, and 80 of the 100 keys end up on a different node.

Why does that matter in production?

It depends on what the servers hold.

If it's a cache

A moved key is usually a cache miss. One miss is harmless, but millions of keys remapping at once is not. The new owners don't have those values yet, so requests fall through to the database, and a routine scaling event turns into this:

cache misses --> database spike --> higher latency --> overloaded database

This is one reason cache scaling needs care.

If it's stored data

Now it gets harder. If a key moves from Node A to Node B, Node B can't serve that record until the data is copied over. While the copy runs:

  • new writes still have to work

  • replicas have to stay consistent

  • the old copy can't disappear too early

  • clients have to agree on who owns the key

Changing the routing formula is easy. Safely moving the state behind it is the hard part.

Modulo sharding works fine when the shard count is fixed. It gets expensive when nodes are expected to join and leave regularly.

How consistent hashing works

A key belongs to the first node encountered while moving clockwise on the ring.

Consistent hashing changes the placement model. Instead of computing hash(key) % number_of_nodes, you put nodes and keys in the same hash space and wrap that space into a circle, usually called a hash ring. A key belongs to the first node you reach moving clockwise from the key's position.

Take a tiny ring with positions 0 to 100 and put four servers on it:

A --> 0
B --> 25
C --> 50
D --> 75

Then hash each key to a position on the ring:

key at 18 --> B
key at 43 --> C
key at 73 --> D
key at 91 --> wraps around --> A

So the ownership ranges look roughly like this:

B owns (0, 25]
C owns (25, 50]
D owns (50, 75]
A owns (75, 100], wrapping back to 0

Adding a fifth node

This is where consistent hashing becomes useful.

Now add E at position 62.5. Before E joined, D owned (50, 75]. After:

E owns (50, 62.5]
D owns (62.5, 75]

Only the keys in that one slice of the ring change owners, and everything else stays where it was. With our 0 to 99 keys, keys 51 through 62 move from D to E. That's 12 keys.

Placement strategy

What changes

Keys moved

Modulo hashing

% 4 --> % 5

80/100

Consistent hashing

One new ring position

12/100

The 12 comes from where we deliberately placed E. In a well-balanced five-node ring, adding one equally weighted node would typically move roughly one-fifth of the keys. Whatever the exact share, adding a server only touches part of the keyspace, and the rest of the cluster keeps serving the same keys. That stability is the "consistent" in consistent hashing.

Virtual nodes

Instead of giving each physical server one position on the ring, give it several. These positions are called virtual nodes, or vnodes. So instead of:

A -------- B -- C ---------------- D

you get something closer to:

A -- C - B --- D - A -- B ---- C - D

There are still only four physical servers, but each one owns several smaller ranges instead of one large one. That improves distribution, and it helps when a server joins. The new server takes small ranges from several existing servers instead of one giant range from a single neighbour, which spreads the rebalancing work across the cluster. Systems inspired by Dynamo and Cassandra have used this idea extensively.

What vnodes cost

If more vnodes means better distribution, why not use as many as possible? Because every extra token adds metadata and more range relationships for the system to track. In replicated systems, it can also add work during data streaming, repair, node replacement, membership changes, and failure recovery.

The right vnode count depends on cluster size, replication factor, the implementation, node capacity, migration bandwidth, and operational overhead.

Vnodes don't fix hot keys

Say one key is customer_123, and that customer suddenly produces 40% of all requests. That key still has one primary owner. You can spread a million normal keys perfectly and still overload one server because a single key is extremely hot.

equal key distribution ≠ equal traffic distribution

The same goes for bytes stored, CPU usage, latency, and request rate. A ring can look perfectly balanced by key count while one node is melting.

What the ring diagram leaves out

Most explainers stop at "add a node, only some keys move." That covers the placement algorithm, but it doesn't explain how the system survives the move. A distributed system has three separate problems here.

1. Placement

Who should own this key? Consistent hashing helps answer that: user_123 --> Node C.

2. Replication

Say you want three copies of every key. Where do the other two live? You can't just pick random nodes without thinking about failure domains. Three replicas in the same rack or availability zone don't protect you much if that whole zone goes down. Real systems may account for physical hosts, racks, availability zones, and regions. Consistent hashing alone doesn't handle this.

3. Rebalancing

This is the operational part. A key used to belong to Node D, and after a new node joins it belongs to Node E. That doesn't mean E has the data yet. The system still has to move it safely. A simplified migration might look like this:

  1. Prepare the new owner.

  2. Copy or rebuild the affected data.

  3. Catch up on writes that happened during the copy.

  4. Verify the new copies.

  5. Move traffic to the new owner.

  6. Remove the old copy once the system is stable.

Different databases do this differently. Some use redirects, routing-table updates, streaming, write fencing, dual writes, or background repair.

Consistent hashing limits what has to move. It doesn't do the moving. That distinction comes up a lot in both system design and production debugging.

Real systems don't all use the same model

"Consistent hashing" is often used loosely, and different systems solve this problem in different ways.

Cassandra is heavily influenced by Dynamo-style partitioning. Nodes own token ranges, and replication decides which other nodes store copies. Modern Cassandra deployments may use multiple tokens per physical node.

Redis Cluster works differently. It divides the keyspace into 16,384 fixed hash slots. Keys map to slots, and slots map to nodes. When the cluster changes, Redis moves specific slots and their keys between nodes. That gives operators explicit migration units instead of a plain ring.

Ceph adds another layer of indirection. Objects map to placement groups, and placement groups map to storage devices. The goal is still to change the cluster without moving everything.

Different system, same underlying distributed-systems problem.

Consistent hashing isn't always the right answer

Modulo hashing isn't bad. It's a good choice when your shard count rarely changes, migrations are planned, simplicity matters, or you can afford to reshuffle now and then.

Consistent hashing becomes more attractive when nodes regularly join or leave, remapping keys is expensive, cache misses are costly, or large-scale data movement is undesirable.

Other strategies fit other requirements:

  • Range-based sharding works when queries need ranges or locality, for example users 1 to 1M on shard A and users 1M to 2M on shard B.

  • Fixed slots work when operators want explicit migration units. Redis Cluster is a good example.

  • Rendezvous hashing is often useful for request routing without maintaining a traditional ring.

  • Topology-aware placement matters when rack, zone, or region failures are a bigger risk than uneven distribution.

No placement algorithm wins everywhere. The right one depends on your workload.

If you remember only four things

  1. Modulo hashing works well until your node count changes. Changing % 4 to % 5 can remap most of your keys.

  2. Consistent hashing limits the blast radius. Adding or removing a node changes only part of the keyspace.

  3. Virtual nodes improve distribution. They spread a physical server's ownership across several sections of the ring.

  4. Consistent hashing only solves placement. Replication, data migration, failure recovery, and hot keys are separate problems.

👋 Find me on Twitter | Linkedin | Connect 1:1

Thank you for supporting this newsletter.
Y’all are the best.

Reply

Avatar

or to participate