Skip to content

shard-owners: scope whole-keyspace commands (KEYS/SCAN/DBSIZE/RANDOMKEY/FLUSH) to the connecting shard for Redis Cluster parity #526

Description

@ELares

Context (#517 shard-owners, found by the PR4 adversarial review)

In cluster_mode = shard-owners the node advertises its N internal shards as N cluster nodes (one per port). The keyed data path matches Redis Cluster exactly (owner port serves locally, wrong port gets -MOVED, cross-slot gets -CROSSSLOT). But the WHOLE-KEYSPACE commands are exempt from cluster redirect (CommandClass::WholeKeyspace returns early in cluster_redirect, serve.rs) and fan out across ALL internal shards regardless of which per-shard port the request arrived on:

  • KEYS / SCAN return the full keyspace from every port (a per-node aggregator sees every key N times)
  • DBSIZE returns the GLOBAL count from every port (a per-node sum over-counts by N)
  • RANDOMKEY samples globally instead of node-locally
  • FLUSHDB / FLUSHALL flush everything from any port (matches operator intent, but a per-node flush tool would flush N times -- idempotent, harmless)

A real Redis Cluster node answers these only for ITS OWN slots. redis-cli --cluster, and cluster client libraries that iterate SCAN per node, will observe the divergence.

Why the fix is natural post-#520

Since the slot-owner alignment (#520), shard i's store contains EXACTLY the keys in its slot range (every write routes to the owning shard). So "scope whole-keyspace commands to the connecting port's shard" is precisely "answer from the HOME shard only, skip the fan-out" -- the data is already partitioned correctly. No per-key slot filtering is needed.

Proposed change

In shard-owners mode only (ctx.cluster_mode() == ShardOwners), route WholeKeyspace commands as HOME-ONLY instead of fan-out:

  • DBSIZE / KEYS / RANDOMKEY: read the home shard's store only.
  • SCAN: the composite cursor already encodes the shard index; in shard-owners mode pin the cursor to the home shard (start there, end when the home shard is exhausted rather than advancing to the next shard).
  • FLUSHDB / FLUSHALL: home-only to match per-node Redis Cluster semantics (an operator flushing the whole dataset uses a cluster-aware tool that visits every node, exactly as with real Redis Cluster).
  • Static/Raft/non-cluster modes: byte-unchanged (fan-out stays).

Also noted (pre-existing, lower priority)

Sharded pub/sub (SPUBLISH/SSUBSCRIBE) broadcasts across all shards rather than per-slot-owner (a fidelity gap delivering a superset of messages; no loss). Consider scoping alongside this work or splitting out.

Acceptance

  • In shard-owners mode with N=4: DBSIZE on port base+i returns only shard i's count; the sum over the 4 ports equals the total; KEYS * per port returns disjoint sets whose union is the keyspace; SCAN on a port enumerates exactly that port's keys and terminates; FLUSHDB on one port leaves the other shards' keys intact.
  • Static mode: all whole-keyspace tests pass unchanged.
  • The CHANGELOG "KNOWN DIVERGENCE" note is replaced by the fixed semantics.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions