beprodready
Build Your Own Consistent Hash Ring

Stage 1: The Basic Ring

stage 1 of 3 · ~20 min · runs in your browser

Objective:Build the ring data structure that assigns keys to nodes with O(log N) lookup and a mathematical guarantee that only 1/N keys migrate when a node joins or leaves.

Hash ring as sorted arrayBinary search for clockwise successorWrap-around (ring topology)O(log N) key lookupDeterministic hashing

The ring is a sorted array of { position, nodeId } entries. Positions are integers in [0, 2^32). Nodes and keys both get hashed to positions; the ring is the sorted union of all node positions.

addNode(nodeId): Hash the node ID to a position in [0, 2^32). Insert { position, nodeId } into the sorted array, maintaining sort order.

removeNode(nodeId): Filter out all entries with this node ID.

getNode(key): Hash the key to a position. Find the first ring entry whose position is >= the key position (binary search). If none exists (the key falls past the last node), wrap around to ring[0]. Return that entry's nodeId, or null if the ring is empty.

The hash function provided in the starter code is deterministic but not cryptographic -- good enough for correctness testing and demonstration. In production you would use MurmurHash3 or MD5 (ketama).

Implement createRing() returning:

  • addNode(nodeId) -- place the node on the ring
  • removeNode(nodeId) -- remove the node and all its positions
  • getNode(key) -- return the owning nodeId, or null if the ring is empty
  • nodes getter -- the full sorted ring array (for inspection)
Why this matters in production

With naive `hash(key) % N`, adding one server to a 10-node cluster remaps 90% of all keys in a single operation. Every remapped key is simultaneously a cache miss, a thundering herd on the backing database, and a latency spike visible to users. Consistent hashing replaces this with a ring: nodes and keys share the same 0-to-2^32 address space. Each key belongs to the nearest clockwise node. When a node is added, it claims an arc of the ring, and only the keys in that arc migrate -- roughly 1/N. When a node is removed, its keys flow to the next clockwise node. No other nodes are affected. This is not an approximation; it is a provable bound from the geometry of the circle, and it is why DynamoDB, Cassandra, and Memcached all make the same architectural choice.

tests (7)

getNode returns null on empty ring

single node gets all keys

second node takes some keys

getNode is deterministic for the same key

removing a node reassigns its keys to the next clockwise node

key wraps around when hash exceeds all node positions

nodes getter returns sorted positions

createRing.js