What to build
SCAN (and scan_iter) is a single-node command in Redis — under Cluster it only sees keys on the node it was issued against. Add a cluster-aware scan wrapper used by all metrics/snapshot.py/admin-CLI code instead of calling scan_iter directly: it must enumerate every master node and merge results, so callers get the same complete result set in standalone and cluster modes. Verify against the actually-pinned redis.asyncio.cluster.RedisCluster version whether .scan_iter() already aggregates across nodes internally (don't assume — check the pinned version's docs/source or test it) — if it doesn't, wrap it so it does.
Separately, _batch_fetch_admin_states reads multiple different workers' worker_admin(id) keys in one pipeline — another cross-entity multi-key operation (read-only, but still CROSSSLOT-prone under Cluster). Replace it with concurrent independent per-worker requests via asyncio.gather(*[redis.hgetall(RedisKeys.worker_admin(wid)) for wid in worker_ids]) rather than relying on unverified pipeline(transaction=False) cross-slot routing behavior.
Acceptance criteria
Blocked by
#45
What to build
SCAN(andscan_iter) is a single-node command in Redis — under Cluster it only sees keys on the node it was issued against. Add a cluster-aware scan wrapper used by allmetrics/snapshot.py/admin-CLI code instead of callingscan_iterdirectly: it must enumerate every master node and merge results, so callers get the same complete result set in standalone and cluster modes. Verify against the actually-pinnedredis.asyncio.cluster.RedisClusterversion whether.scan_iter()already aggregates across nodes internally (don't assume — check the pinned version's docs/source or test it) — if it doesn't, wrap it so it does.Separately,
_batch_fetch_admin_statesreads multiple different workers'worker_admin(id)keys in one pipeline — another cross-entity multi-key operation (read-only, but still CROSSSLOT-prone under Cluster). Replace it with concurrent independent per-worker requests viaasyncio.gather(*[redis.hgetall(RedisKeys.worker_admin(wid)) for wid in worker_ids])rather than relying on unverifiedpipeline(transaction=False)cross-slot routing behavior.Acceptance criteria
metrics/snapshot.py/admin code currently callsscan_iterdirectly_batch_fetch_admin_statesconverted from pipeline toasyncio.gatherof independent requestsBlocked by
#45