Rendezvous hashing, also called highest random weight hashing, maps a key to the node with the highest score for that key. Each candidate node is scored together with the key, and the node with the maximum score is selected.
You use it in distributed systems when keys must be assigned to changing sets of nodes with minimal movement and no shared hash ring.
Problem
Given a set of nodes and a key , choose the node responsible for .
Model
For every node , compute a score:
The selected node is:
The hash function should distribute scores uniformly.
Algorithm
rendezvous_lookup(nodes, k):
best_node = null
best_score = -infinity
for each node in nodes:
score = h(node, k)
if score > best_score:
best_score = score
best_node = node
return best_nodeExample
Suppose the nodes are:
For key "user:42", assume the scores are:
| node | score |
|---|---|
| A | 18 |
| B | 73 |
| C | 41 |
The highest score is 73, so the key maps to node B.
If node B is removed, the key moves to the remaining node with the next highest score, which is C in this example.
Correctness
The algorithm computes the score for every candidate node and keeps the maximum. Therefore it returns exactly the node defined by the rendezvous hashing rule.
When a node is added or removed, only keys for which that node wins or previously won need to move. Other keys keep the same winner.
Complexity
| operation | time |
|---|---|
| lookup |
The lookup scans all nodes. Optimized variants reduce this cost for large node sets.
Space
Lookup uses only the current best node and score, aside from the node list itself.
When to Use
Rendezvous hashing is appropriate when:
- nodes change over time
- key movement should be minimal
- implementation simplicity matters
- each client can know the node list
- weighted node assignment may be needed
It is common in distributed caches, sharded storage, request routing, and replicated systems.
Implementation
import hashlib
def score(node, key):
data = f"{node}:{key}".encode()
digest = hashlib.blake2b(data, digest_size=8).digest()
return int.from_bytes(digest, "big")
def rendezvous_lookup(nodes, key):
best_node = None
best_score = -1
for node in nodes:
s = score(node, key)
if s > best_score:
best_score = s
best_node = node
return best_nodeimport (
"encoding/binary"
"hash/fnv"
)
func rendezvousScore(node string, key string) uint64 {
h := fnv.New64a()
h.Write([]byte(node))
h.Write([]byte{0})
h.Write([]byte(key))
return binary.BigEndian.Uint64(h.Sum(nil))
}
func RendezvousLookup(nodes []string, key string) string {
bestNode := ""
var bestScore uint64
for i, node := range nodes {
score := rendezvousScore(node, key)
if i == 0 || score > bestScore {
bestScore = score
bestNode = node
}
}
return bestNode
}