Skip to content

CLUSTER SLOT-STATS (8.2): per-slot usage statistics on IServer, experimental SER012 - #3341

Merged
mgravell merged 2 commits into
mainfrom
marc/cluster-slot-stats
Nov 9, 2026
Merged

mgravell merged 2 commits into
mainfrom
marc/cluster-slot-stats

Conversation

@mgravell

@mgravell mgravell commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator

Adds first-class support for CLUSTER SLOT-STATS (Redis 8.2): per-slot usage statistics, so callers can see which slots carry a cluster's load. Promotes the deferred row in CommandCoverage ("pairs with hot-key tooling" — the hot-key tooling has since shipped).

API (all experimental, SER012)

// the two server forms, as two IServer entry points (strict oneof grammar; overloads would trip RS0026)
ClusterSlotStatsResult? ClusterSlotStatsBySlotRange(int startSlot, int endSlot, ...);          // SLOTSRANGE start end
ClusterSlotStatsResult? ClusterSlotStatsOrderBy(ClusterSlotStatsMetric metric,
    long limit = 0, Order order = Order.Descending, ...);                                      // ORDERBY metric [LIMIT n] [ASC]

ClusterSlotStatsEntry carries Slot, KeyCount (always collected), and CpuTime (TimeSpan?, from cpu-usec), MemoryBytes, NetworkBytesIn/Out as nullable values: the server only reports what cluster-slot-stats-enabled collects, and "not collected" is deliberately distinct from a collected zero. Metrics the library doesn't recognize are preserved in Metadata (the metric set is documented as extensible, and has grown before).

Design grounding

  • File layout, message, nil handling: traced from CLUSTER SLOTS (ClusterSlots.cs siblings) — partial-interface file per feature, Message.Create(-1, flags.WithCategory(NodeLocalRead), RedisCommand.CLUSTER, ...), lenient aggregate parsing, null for nil.
  • Reply-field handling: traced from HOTKEYS — internal [AsciiHash] field enum with generated TryParse, nominal (never positional) parsing, microsecond counters surfaced as TimeSpan, limit = 0 meaning "server default" as HotKeysStart(count = 0) does.
  • Minimal wire form: LIMIT sent only when limit > 0, ASC only when ascending — the server defaults are expressed by omission.
  • Versioning: RedisFeatures.ClusterSlotStats gates on the existing v8_2_0_rc1 constant. Pre-8.2 answers ERR Unknown subcommand.
  • SER012 is a feature-specific experiment id (SER011 is Server 8.12 on the open Set-member expiration (Redis 8.12): SADDEX / SEXPIRE / SPTTL / SPERSIST on IDatabase #3340; keeping them separate avoids coupling the PRs).

Plan with observed server facts (verified against src/cluster_slot_stats.c and a live cluster): notes/cluster-slot-stats/plan.md.

Tests

  • Round-trip (RoundTripUnitTests/ClusterSlotStatsRoundTrip.cs, 10/10): exact request bytes for both forms including LIMIT/ASC minimality; the full metric map parsed identically from RESP2 flat pairs and a RESP3 map; key-count-only replies leave the gated metrics null; an unknown metric lands in Metadata; empty array and nil replies; argument validation.
  • Live (ClusterSlotStatsTests.cs, 12/12 against the repo cluster topology, per protocol, zero skips): a routed write is counted in its slot by the owning node; whole-range answers stay shard-local and ascending; ORDERBY honours LIMIT and both orders; CONFIG SET cluster-slot-stats-enabled "cpu net" surfaces CpuTime/NetworkBytes* at runtime (with MemoryBytes still null, config restored in finally); ORDERBY on the disabled mem metric gets the server's ORDERBY refusal; the standalone node answers "cluster support disabled".

Full solution build clean (/p:CI=true, 0 warnings) across net461/net472/netstandard2.0/net8.0/net10.0; eng/public-api.py --check passes.

Note: RS0016 does not fire in my local environment, so the PublicAPI.Unshipped.txt entries are hand-written against the shipped [SER009] / #3340 [SER011] precedents and sorted with eng/public-api.py — worth a glance.

…imental SER012

IServer.ClusterSlotStatsBySlotRange(Async) and ClusterSlotStatsOrderBy(Async), modelled
on the CLUSTER SLOTS file layout and the HOTKEYS field handling: node-local read, lenient
nominal parsing, config-gated metrics as nullable properties (absent != zero), unknown
metrics preserved as Metadata, cpu-usec surfaced as TimeSpan. RedisFeatures.ClusterSlotStats
gates on 8.2 RC1. Promotes the CommandCoverage 'deferred' row.
@mgravell
mgravell marked this pull request as ready for review November 9, 2026 11:15
@mgravell
mgravell merged commit ebe6037 into main Nov 9, 2026
7 of 8 checks passed
@mgravell
mgravell deleted the marc/cluster-slot-stats branch November 9, 2026 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant