Consistent Hash CLI
A Node CLI that builds a consistent hash ring from a node list and routes keys to nodes, demonstrating minimal remapping on node add or remove.
Project
node-consistent-hash-cli
Entry point: chash.mjs
Install
node --versionRun
node chash.mjs "a,b,c" key1 key2 key3Test
node --test chash.test.mjsExpected output
Prints the node that each key maps to on the consistent hash ring built from the comma-separated node list.Dependencies
- node >=20
What it does
Builds a consistent hash ring from a comma-separated node list, using 100 virtual nodes per physical node for balance. Each key is hashed and routed to the first node clockwise on the ring via binary search. The virtual nodes spread each physical node across the ring so the distribution is balanced.
import { createHash } from 'node:crypto';
const nodes = process.argv[2].split(',');
const keys = process.argv.slice(3);
const V = 100;
const ring = [];
for (const n of nodes) for (let i = 0; i < V; i++) ring.push([+('0x' + createHash('sha256').update(n + ':' + i).digest().readBigUInt64BE().toString()), n]);
ring.sort((a, b) => a[0] < b[0] ? -1 : 1);
const route = k => {
const h = +('0x' + createHash('sha256').update(k).digest().readBigUInt64BE().toString());
let lo = 0, hi = ring.length;
while (lo < hi) { const mid = (lo + hi) >> 1; if (ring[mid][0] < h) lo = mid + 1; else hi = mid; }
return ring[lo % ring.length][1];
};
keys.forEach(k => console.log(k + ' -> ' + route(k)));
Run and verify
node chash.mjs "a,b,c" hello world foo
Run the test command to check the implementation: node --test chash.test.mjs.
Expected Output
Each key is mapped to a node on the ring:
hello -> b
world -> a
foo -> c
The exact mapping depends on the hash values but is deterministic for the same node list and keys.
Limitations
The ring is rebuilt on every run, so it is a demonstration. The virtual node count is fixed at 100; a higher count improves balance at the cost of a larger ring.