Cluster — 16384 slots and the client routes

Redis Cluster shards by CRC16(key) mod 16384 with no proxy; clients cache a slot map and learn corrections via MOVED (permanent) vs ASK (transient, per-query), and configEpoch resolves slot-ownership conflicts after failover.

Previously

Sentinel buys availability with a single master. But you still only have one master's worth of write throughput. Cluster splits the keyspace across multiple masters — and pushes the routing logic into the client.

Scene 08

Cluster — 16384 slots and the client routes

  1. Watch
  2. Try it
  3. Predict
  4. Capture
SLOTS · 16384 (CRC16(key) mod 16384)0–10231024–20472048–30713072–40954096–51195120–61436144–71677168–81918192–92159216–1023910240–1126311264–1228712288–1331113312–1433514336–1535915360–16383PSYNCB1 (master)MASTERslots: 0–6143configEpoch: 1B1 replicaREPLICAPSYNCB2 (master)MASTERslots: 6144–11263configEpoch: 1B2 replicaREPLICAPSYNCB3 (master)MASTERslots: 11264–16383configEpoch: 1B3 replicaREPLICAClientsmart clientCACHED SLOT MAPclient believes ↓
What to watch for

The slot bar across the top is colored by owning master. The client holds a cached copy of that map. Watch a few queries hash to slots and land on the right master in one round trip.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

Client.send # MOVED handling
permanent cache heal on -MOVED
def send(cmd, key):
slot = hashSlot(key)
node = cachedMap[slot] # client-side route
resp = node.exec(cmd)
if resp is -MOVED(slot, newOwner):
cachedMap[slot] = newOwner # heal permanently
return newOwner.exec(cmd) # retry once
return resp
Client.send # ASK handling
transient redirect — cache is NOT updated
def send(cmd, key):
slot = hashSlot(key)
node = cachedMap[slot]
resp = node.exec(cmd)
if resp is -ASK(slot, target):
# one-shot: prefix ASKING, route to target
target.exec(ASKING) # ONCE
return target.exec(cmd)
# cachedMap[slot] is left UNCHANGED
return resp
hashSlot # CRC16 with hash tag
{tag} forces multi-key commands to one slot
def hashSlot(key):
lo = key.find('{')
if lo != -1:
hi = key.find('}', lo + 1)
if hi > lo + 1:
key = key[lo + 1 : hi] # hash only the tag
return crc16(key) % 16384
Master.handleQuery # slot ownership check
MIGRATING → -ASK; not-mine → -MOVED
def handleQuery(cmd, key):
slot = hashSlot(key)
if slot not in myOwnedSlots:
return -MOVED(slot, owners[slot])
if slot in MIGRATING and key not local:
return -ASK(slot, migrating[slot].target)
if slot in IMPORTING and not asking_flag:
return -MOVED(slot, owners[slot])
return execute(cmd, key)

Where this sits in Build Redis

Scene 08 of 10, in the Shard & ship act — Cluster slots, MOVED/ASK, and the design canvas.. CRC16 mod 16384, MOVED vs ASK (permanent vs transient), hash tags, configEpoch — and why sharding alone is not HA.

Up next. Design canvas — you've seen every knob. Now assemble them into a real deployment and name the trade you're making, out loud, against the SLO.

All 10 scenes in Build Redis · Every curriculum

Built with Arqly
Every scene in Build Redis builds on the one before it.All 10 Build Redis scenes