Chapter 21 · Redis Cluster: Hash Slots, Routing, Resharding, and Cluster Availability
MOVED vs ASK Redirections, Cluster-Aware Clients, Topology Refresh, and Retry Behavior
Observe MOVED and ASK routing directly, then use a cluster-aware client with bounded topology refresh and retry behavior.
Learning outcomes
By the end of this lesson, you should be able to:
Distinguish permanent MOVED redirections from
one-command ASK redirections during migration.
Explain why ASK must be followed by ASKING on
the same connection before the redirected command.
Use a cluster-aware redis-py client that maintains a slot map and refreshes it after topology change.
Treat retries as bounded routing recovery, not permission to replay non-idempotent application side effects forever.
Observe classic MIGRATING/IMPORTING state while understanding that Redis 8.10 redis-cli resharding uses newer atomic migration.
Redis Open Source 8.10.1 using
redis:8.10.1; seven named containers are defined
but the normal cluster starts six nodes (three primaries + three
replicas) on private Docker network
atlasmart-redis-ch21-net; the seventh node is
started only for add/remove exercises; Redis Cluster uses
logical database 0 only; AOF everysec;
maxmemory 0/noeviction for this
bounded lab; TLS is off only because all Cluster client/bus
traffic is confined to one private single-host Docker network;
default ACL user is disabled; named
academy-admin and atlasmart-app users
use disposable lab passwords. No
Search/JSON/vector/time-series/probabilistic feature is
required. All keys use atlasmart:ch21:*.
1. Practical problem: the right key sent to the wrong node
A non-aware client knows only one host. In Cluster, that host may not own the target key’s slot. Redis does not proxy every operation through that node; it returns a redirection so the client can contact the owner directly. This avoids a central routing bottleneck but makes topology awareness part of client correctness.
2. MOVED means update the stable slot map
A -MOVED slot endpoint reply means the contacted
node believes another node is the current owner of that slot. A
complete Cluster client should retry at the supplied endpoint
and update or refresh its slot table. One MOVED often signals
broader topology change, so current guidance allows refreshing
from CLUSTER SHARDS.
# This key is intentionally chosen to hash to slot 0, initially owned by n1.KEY=atlasmart:ch21:l2:ask:69430docker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n1 redis-cli --user academy-admin CLUSTER KEYSLOT "$KEY"# Ask n2 directly, without -c. Expected: MOVED 0 atlasmart-redis-ch21-n1:6379docker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n2 redis-cli --user academy-admin GET "$KEY"
3. ASK is temporary migration routing, not new ownership
During classic live migration, the source marks the slot
MIGRATING and the destination marks it
IMPORTING. Existing keys may still live on the
source. A request for a key that is absent on the source can
receive ASK, meaning: send only this next operation
to the destination, prefix it with ASKING, but do
not permanently rewrite the slot map yet.
4. Bounded ASK demonstration on slot 0
The following lab does not move production data. It stages slot 0 between n1 and n2, keeps one existing key on n1, asks for a different missing key in the same slot to expose ASK, then returns both nodes to STABLE. The two key strings were selected because both hash to slot 0 in Redis Cluster.
EXISTING=atlasmart:ch21:l2:ask:69430MISSING=atlasmart:ch21:l2:ask:78471SRCID=$(docker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n1 redis-cli --user academy-admin --raw CLUSTER MYID)DSTID=$(docker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n2 redis-cli --user academy-admin --raw CLUSTER MYID)docker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n1 redis-cli --user academy-admin SET "$EXISTING" source-valuedocker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n2 redis-cli --user academy-admin CLUSTER SETSLOT 0 IMPORTING "$SRCID"docker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n1 redis-cli --user academy-admin CLUSTER SETSLOT 0 MIGRATING "$DSTID"docker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n1 redis-cli --user academy-admin GET "$EXISTING"# Expected for the missing key: ASK 0 atlasmart-redis-ch21-n2:6379docker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n1 redis-cli --user academy-admin GET "$MISSING"# ASKING and GET must be on one connection.printf 'ASKING\nGET %s\n' "$MISSING" | docker exec -i -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n2 redis-cli --user academy-admindocker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n1 redis-cli --user academy-admin CLUSTER SETSLOT 0 STABLEdocker exec -e REDISCLI_AUTH=AtlasMart-Ch21-Admin-Lab-Only-2026 atlasmart-redis-ch21-n2 redis-cli --user academy-admin CLUSTER SETSLOT 0 STABLE
5. Version boundary: redis-cli 8.10 migration is newer than the teaching state machine
Redis 8.4 introduced CLUSTER MIGRATION for
server-side atomic slot migration. Redis 8.10 release notes
state that redis-cli --cluster reshard and
rebalance now use server-side atomic slot
migration. Therefore a normal 8.10 reshard may not expose a
long-lived classic MIGRATING/IMPORTING window. Clients must
still understand MOVED/ASK semantics for compatibility and
mixed-version/topology transitions.
6. A cluster-aware client owns the retry state machine
A complete client computes slots, maintains node/slot topology,
follows MOVED, performs ASKING+command for ASK, refreshes stale
maps, reconnects, and bounds retries. redis-py provides
RedisCluster. Run it inside the same Docker network
because the nodes announce Docker hostnames.
from redis.cluster import RedisClusterrc = RedisCluster( host="atlasmart-redis-ch21-n1", port=6379, username="atlasmart-app", password="AtlasMart-Ch21-App-Lab-Only-2026", decode_responses=True, socket_timeout=1.0,)for i in range(20): key=f"atlasmart:ch21:l2:item:{i}" rc.set(key, f"v{i}") print(key, rc.get(key))print("nodes", len(rc.get_nodes()))rc.close()
docker run --rm --network atlasmart-redis-ch21-net -v "$PWD:/work" -w /work python:3.13-slim \ sh -lc 'pip install --disable-pip-version-check redis==8.1.0 && python cluster_client.py'
7. Retry boundary: routing replay is not business idempotency
A client may safely retry a read after MOVED because the first attempt was rejected before execution. Timeouts are different: a write may have executed even if its reply was lost. Do not put an external payment capture inside an infinite “retry on Cluster error” loop. Use bounded client retries, idempotency keys/state machines, and application reconciliation for side effects.
8. Topology refresh and dynamic endpoints
redis-py can patch the moved slot after a MOVED error and periodically/repeatedly reinitialize topology depending on its settings. Dynamic startup nodes and address remapping are client-specific. The correct production setting depends on DNS, NAT/proxying, announced endpoints, maintenance behavior, and client library version.
9. Observability checklist
Record MOVED/ASK counts, topology refreshes, retry counts,
connection errors, p50/p95/p99 latency, and the current
CLUSTER SHARDS map. A spike in redirections during
resharding may be expected; persistent redirections after the
cluster is stable can signal a stale or non-aware client.
Check your understanding
- What does MOVED tell a client?
- What does ASK tell a client?
- Why must ASKING and the command share a connection?
- Does a timeout prove the write did not execute?
Review the answers
The slot is now owned by another endpoint; retry there and update/refresh the stable slot map.
Temporarily send only the next operation to the destination with ASKING; do not permanently move the slot in the local map yet.
ASKING sets one-shot connection state consumed by the immediately following command.
No. Routing errors rejected before execution differ from ambiguous network timeouts after execution may have occurred.
10. Production judgment and bridge
Choose a maintained Cluster-aware client and test it during resharding, failover, DNS/address changes, and connection loss. Do not implement ad-hoc MOVED parsing unless building a client library. Lesson 3 now focuses on data modeling: which keys must share a slot and which should deliberately remain distributed.
Summary and next step
MOVED vs ASK Redirections, Cluster-Aware Clients, Topology Refresh, and Retry Behavior is now connected to observable Redis behavior, bounded failure cases, and production tradeoffs. Keep the evidence and cleanup state from this lesson; next, continue with Hash Tags and Multi-Key Operations: Co-Locating Related Keys Without Creating Hot Slots.
Authoritative references
- Redis Cluster specification
- Scale with Redis Cluster
- CLUSTER SHARDS
- CLUSTER SLOTS — deprecated since 7.0
- CLUSTER INFO
- CLUSTER NODES
- CLUSTER KEYSLOT
- CLUSTER SETSLOT
- CLUSTER MIGRATION
- CLUSTER SLOT-STATS
- CLUSTER FAILOVER
- ASKING
- MIGRATE
- Redis 8.10 release notes
- Redis 8.10 — what is new
- redis-py — connect to Redis Cluster
- redis-py 8.1.0 cluster documentation