Beyond Code & Karma · System Design

Consistent Hashing

Spread keys across a changing set of servers and move as few as possible when one joins or leaves. The hash ring behind every distributed cache and sharded database — explained through clockwise ownership, virtual nodes, and the Kaal Chakra, the wheel that turns without reshuffling the world.

Interactive Deep Dive · The Hash Ring

Don't divide by N.
Place everything on a circle.

Consistent hashing spreads keys — cache entries, database rows, sessions — across a set of nodes so that adding or removing a node moves as few keys as possible. Instead of mapping a key to a node by arithmetic on the node count, you hash both keys and nodes onto the same circular space — the ring — and give each key to the first node you meet travelling clockwise. The node count is no longer in the formula, so changing it barely disturbs anything.

The whole idea in one line: map keys and servers to points on a circle; a key belongs to the next server clockwise. Add or remove a server and only one arc of keys re-homes — roughly 1/N — while every other key stays exactly where it was.
The Ring
A circular hash space (think a 32-bit number wrapped end-to-end). Both keys and nodes hash to positions on it — one shared coordinate system.
Clockwise Ownership
A key walks clockwise to the first node it meets. That node owns it — deterministic, no lookup table, no coordinator to ask.
Virtual Nodes
Each server sits on the ring many times. Many small arcs per server flatten the lumpy load a single position would create.
Minimal Remapping
Add or drop a node and only its arc moves — about 1/N of keys. The other (N-1)/N never notice the change.

The Kaal Chakra — the wheel that turns without reshuffling the world

Hindu Philosophy Parallel

The Kaal Chakra — the great wheel of time — turns eternally, and yet what is in its right place is not flung aside each turn. The ring of consistent hashing is this same mandala: a circle of duty where each node is the guardian of one arc, and every key finds its rightful keeper by walking clockwise. When a guardian departs — anitya, impermanence — only its arc is handed to the neighbour at hand. The wheel turns; the world is not remade.

Mandala — the ring
The eternal circle. Every key and every guardian holds a fixed seat on the same turning wheel.
Avatars — virtual nodes
One deity, many forms. A single server takes many seats around the ring so no arc grows too heavy for one.
Dharma — minimal remapping
Order preserved through change. A guardian leaves; only its arc passes on. Everything already in place stays in place.

"The wheel turns, and the disturbance stays local — that is dharma, and it is also good distributed-systems design."

The hash % N rehash storm

The obvious scheme is node = hash(key) % N. Perfectly balanced, trivially simple — until N changes. Because N is the modulus, adding or removing one node changes the result for almost every key: on average (N-1)/N of all keys move. With 10 nodes, growing to 11 re-homes about 90% of the keyspace at once — a cache-miss stampede onto your database, or a network-saturating reshuffle of a sharded store.

Step one — a key's place on the ring

Consistent hashing breaks the dependency on N. First, hash anything — a key or a server label — to a position on the circle:

hash-to-ring.js
// Map any string onto the ring — a point in the space [0, 2³²).
function hashToRing(str) {
  let h = 2166136261 >>> 0;          // FNV-1a, 32-bit seed
  for (let i = 0; i < str.length; i++) {
    h ^= str.charCodeAt(i);
    h = Math.imul(h, 16777619);
  }
  return h >>> 0;                  // the key's position on the circle
}
The key move: N never appears in hashToRing. A key's position is fixed by the key alone, so it doesn't shift just because the node set changed. That single fact is the whole advantage over % N.

Modulo vs consistent — the churn, side by side

Eventhash % NConsistent hashing
Add 1 node (N→N+1)~(N-1)/N of keys move~1/(N+1) of keys move (one arc)
Remove 1 node~(N-1)/N of keys move~1/N of keys move (that node's arc)
Lookup costO(1) arithmeticO(log V) binary search over V virtual nodes
Load balancePerfectly evenEven, given enough virtual nodes

Find the next node clockwise

Hash the key to a point, then sweep clockwise to the first node you hit. Wrap past the top of the circle back to the start. That node is the owner — and it's the same answer on every machine, with no shared state to consult:

owner-of.js
// nodes = virtual-node positions, sorted ascending around the ring.
// A key is owned by the FIRST node clockwise from its position.
function ownerOf(key, nodes) {
  const p = hashToRing(key);
  for (const node of nodes) {
    if (node.pos >= p) return node.server;  // first past the key
  }
  return nodes[0].server;      // walked off the top → wrap to the start
}
// In production this is a binary search — O(log V) over V virtual nodes.

The four phases — how the ring stays calm

Place the nodes

Hash each server — and each of its virtual replicas — to positions on the ring. Sort them once; this is the routing table.

Map the keys

Each key hashes to its own point and walks clockwise to the first node. No coordinator, no lookup table — just the hash and a binary search.

Add or remove a node

The topology changes at just one or a few points on the circle. A new node slips between two neighbours; a leaving node vanishes from the sorted list.

Only K/N keys move

The affected arc re-homes to its clockwise neighbour. Every key outside that arc still resolves to exactly the same node it did before.

Watch a node leave — and only one arc moves

A small ring, auto-playing. It adds a fourth node, then removes it, on a loop. Only the dots whose owner changed light up — count how few move each time.

Hash ring · clockwise ownership
◷ Ready
⛶ Open full ring

The nuances that senior engineers know

This is where the surface-level explainers stop. Here's what actually matters when you run this in production.

With one position per server, the ring is lumpy. By luck, one server can land owning a giant arc and take far more than its share while another starves. The fix is to place each physical server on the ring many times — 100 to 200 replicas at independently hashed positions. Each server now owns many small arcs scattered around the wheel, and the law of averages flattens the load. As a bonus, when a server leaves, its arcs are inherited by many different neighbours instead of dumping the whole load on one.

Three ways to map keys to a changing node set — the cheat-sheet:

SchemeKeys moved on changeTrade-off
hash % N~(N-1)/N — almost allDead simple, O(1), but catastrophic churn
Consistent hashing~1/N — one arcNeeds virtual nodes for balance; O(log V) lookup
Rendezvous (HRW)~1/N — minimalNo ring to maintain; O(N) score per key unless tree-optimised

Rule of thumb: consistent hashing dominates when the node set changes often and you want cheap lookups. Rendezvous hashing wins when you want minimal moves without maintaining a sorted ring, and N is small.

Consistent hashing bounds how many keys move and evens out the keyspace — but it cannot fix skew in the keys themselves. If one key (a celebrity user, a viral product) gets a disproportionate share of requests, that single key still lands on one node and that node still gets hot. Real systems pair the ring with hot-key detection, replication of popular keys, or request coalescing.

Say it in interviews: "Consistent hashing balances the keyspace, not the request distribution." It signals you know the limit, not just the trick.

Under hash % N, a key keeps its node after going to N+1 only if hash % N === hash % (N+1) — which holds for roughly 1 in N keys. So about (N-1)/N of keys change owners. On the ring, a new node only captures the arc between itself and its predecessor — an expected 1/(N+1) slice of the circle — so only that fraction re-homes, and to the new node alone.

It's the default partitioner anywhere keys spread over a changing node set:

SystemWhat it partitions
Memcached clientsCache keys across nodes, so adding a node doesn't cold-start the whole cache.
DynamoDB / CassandraPartitions placed on a ring with virtual nodes for even, low-churn distribution.
Load balancersConsistent-hash balancing pins a client to the same backend (sticky sessions) as the pool changes.
Object / blob storesSpread objects across storage nodes while keeping rebalance minimal on scale-up or failure.

The Karma of minimal disruption

The Karma Connection

Karma is the principle that change is constant, yet what is in its right place need not be undone. No node owns the whole world; each holds only its arc. When a node joins or departs, its duty passes only to the neighbour at hand — not to all.

karma.js
// Add a node: it claims only the arc behind it…
ring.add('node-D');    // ~1/N of keys re-home to D; the rest never move

// …remove a node: its arc passes to the next node clockwise.
ring.remove('node-B'); // only B's keys move — the wheel turns on, undisturbed

This is the discipline of the Kaal Chakra: let the wheel turn without reshuffling everything already settled in its rightful seat. Virtual nodes are many hands sharing one load so none is crushed; minimal remapping is order preserved through impermanence. The ring holds no grudge and plays no favourites — it keeps the disturbance local and moves on.

"Do not turn the whole wheel to seat one new guest. Hand on only the arc at hand." — the Gita, interpreted for distributed systems 🙏

Hash Ring Trivia

Three questions. No cheating. Your karma is watching.

Question 1
You run 4 nodes on a hash ring and add a 5th. Roughly what fraction of keys change owner?
Question 2
Why place each physical server on the ring many times (virtual nodes)?
Question 3 — 🌶️ Spicy
Under naive hash % N with N = 10, you add one server (N → 11). Roughly how many keys must move?

The Six Truths of Consistent Hashing

The Ring Kills N
Map keys and nodes to a circle and the node count leaves the formula. That's why changing it barely disturbs anything.
Clockwise Is Law
A key belongs to the first node clockwise from its position — deterministic, coordinator-free, identical on every machine.
~1/N Moves
Add or remove a node and only its arc re-homes. The other (N-1)/N of keys never notice the change.
Virtual Nodes Balance
Many replicas per server flatten lumpy arcs and spread a departing node's load across many neighbours, not one.
Keyspace, Not Traffic
It balances the keyspace, not the request distribution. A hot key still lands on one node — pair it with replication.
Kaal Chakra — Turn Lightly
The wheel turns; the disturbance stays local. Hand on only the arc at hand, and the system stays calm under change.

कर्म करो, फल की चिंता मत करो