2026-09-10
Plain rendezvous hashing (HRW) picks the node with the highest hash(key, node_id). It gives you clean placement stability — add or remove a node and only 1/N of keys move. But it assumes every node is identical. In the real world, your fleet has a 64GB box next to a 256GB box, and uniform placement means the small box melts while the big one naps.
The fix: weighted rendezvous hashing. Assign each node a weight proportional to its capacity, then score keys with:
score(key, node) = -weight / ln(hash(key, node) / MAX_HASH)
The key goes to the node with the highest score. That logarithm-of-uniform trick is the same math behind weighted reservoir sampling — it produces a placement where each node's share of keys converges exactly to weight_i / sum(weights), and it's stateless: any client can compute placement independently with just the node list.
Concrete example. You run a distributed cache with three nodes: cache-a (128GB), cache-b (128GB), cache-c (256GB). Weights: 1, 1, 2. With plain HRW, all three get ~33% of keys and cache-c wastes half its RAM. With weighted HRW, cache-c gets 50%, the others 25% each. Then you add cache-d (256GB, weight 2). New distribution: 16.7% / 16.7% / 33.3% / 33.3%. Only the keys whose top-scoring node changed move — roughly 33% of keys migrate to cache-d, and no keys shuffle between the other three.
Rule of thumb for weight sizing. Set weight = usable capacity ÷ smallest node's capacity, rounded to one decimal. Don't chase integers — HRW handles fractional weights fine, and coarse rounding costs you balance. If your smallest node has 64GB and a new node has 200GB, use weight 3.1, not 3.
Where it beats consistent hashing. No ring, no virtual nodes to tune, no lookup table to distribute. Adding a heterogeneous node is a config change — bump the node list, everyone recomputes. The tradeoff: O(N) scoring per lookup instead of O(log N). Fine for N < 1000; painful at 10,000+ nodes, where you'd shard the node list into tiers first.
Watch out for: changing a node's weight is not free — it re-scores every key against that node, potentially moving a chunk of traffic. Treat weight as a deploy-time property, not a runtime knob.
-weight / ln(hash) and each node's key share converges to its weight fraction.
