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.

Advanced180–260 minutesMOVED, ASK, ASKING, redis-py RedisCluster, retriesRedis Open Source 8.10.1Docker + redis-cli + redis-py 8.1.06-node Cluster + 1 spare nodeDB 0 · AOF everysec · maxmemory 0/noevictionFree/local-firstLast reviewed: September 6, 2026

Learning outcomes

By the end of this lesson, you should be able to:

01

Distinguish permanent MOVED redirections from one-command ASK redirections during migration.

02

Explain why ASK must be followed by ASKING on the same connection before the redirected command.

03

Use a cluster-aware redis-py client that maintains a slot map and refreshes it after topology change.

04

Treat retries as bounded routing recovery, not permission to replay non-idempotent application side effects forever.

05

Observe classic MIGRATING/IMPORTING state while understanding that Redis 8.10 redis-cli resharding uses newer atomic migration.

Reproducible Chapter 21 baseline

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.

Shell · provoke MOVED with a non-aware client
# 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.

Shell · stage MIGRATING / IMPORTING safely
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.

Python · redis-py 8.1.0 cluster client
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()
Shell · run the client in the Cluster network
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

  1. What does MOVED tell a client?
  2. What does ASK tell a client?
  3. Why must ASKING and the command share a connection?
  4. 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

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.