Skip to main content

Pub/sub introspection

The Server facet exposes PUBSUB CHANNELS, NUMSUB, NUMPAT, SHARDCHANNELS, and SHARDNUMSUB. Ordinary introspection requires Redis 2.8+; sharded introspection requires Redis 7+. Unsupported versions and denied ACL permissions retain server errors. These read-only commands do not require AllowAdmin.

RespireChannel channel = "events";
RespireChannel[] active = await redis.Server.PubSubChannelsAsync(pattern: "event*");
RespireChannelSubscriberCount[] subscribers =
await redis.Server.PubSubSubscriberCountsAsync([channel]);
long uniquePatterns = await redis.Server.PubSubPatternCountAsync();
RespireChannelSubscriberCount[] shardSubscribers =
await redis.Server.PubSubSubscriberCountsAsync([channel], sharded: true);

Channel names and patterns preserve their exact bytes. RespireChannel owns its input storage; result channels also own their storage after the reply and client are disposed. The span of names is copied before asynchronous work. Empty names, empty name lists, and an omitted versus empty pattern remain distinct. The sharded argument selects the command family; the input channel's subscription kind does not change the query. Returned sharded names have SubscriptionKind.Sharded.

Server introspection does not apply WithKeyPrefix to channel names or patterns. It inspects the server's pub/sub namespace. CHANNELS lists channels with literal subscribers, excluding channels with only pattern subscribers. NUMSUB counts literal subscribers, excluding pattern subscribers. NUMPAT counts unique patterns, not the number of clients or matching channels. Sharded and ordinary counts are separate. These are observations at execution time, not a durable delivery guarantee.

Results from every node​

Ordinary methods query one execution node. In Cluster mode they do not imply a global count or route sharded introspection by the supplied channel. Use the explicit OnAllNodesAsync methods to inspect every discovered member, including replicas and primaries without slots:

RespireServerResult<RespireChannelSubscriberCount[]>[] nodes =
await redis.Server.PubSubSubscriberCountsOnAllNodesAsync(["events"]);
foreach (var node in nodes)
{
if (!node.IsSuccess)
{
Console.WriteLine($"{node.Endpoint}: {node.Error!.Message}");
continue;
}
foreach (var item in node.Value)
Console.WriteLine($"{node.Endpoint}: {item.Channel} has {item.Subscribers} subscribers");
}

Each result retains its endpoint and either an owned Value or the original Error. Accessing Value on failure throws InvalidOperationException with that error as its inner exception. Counts are not summed and channel names are not deduplicated across nodes. Standalone execution returns one result. TryGetValue(out var value) provides non-throwing access; on failure it returns false and assigns the default value, while Error retains the original exception.

Cluster discovery uses CLUSTER NODES on a reachable connection, requiring that ACL permission. It keeps the reachable address of the reporting node and uses advertised client endpoints for other members; deployment networking must make those addresses reachable. Unfinished handshake entries are excluded. Missing addresses, malformed or conflicting topology, and discovery failures throw before fan-out starts, so a partial topology is never silently presented as complete. Membership can change after the snapshot; no atomic Cluster-wide observation is promised.

For other members, discovery prefers an advertised hostname when present and otherwise uses the advertised IP address; it does not substitute the seed hostname. TLS certificate validation uses that endpoint host unless TlsOptions.TargetHost explicitly overrides it. Advertised addresses must therefore be reachable and match the deployment's certificate configuration. The reporting node retains the already reachable source address.

Each call re-runs CLUSTER NODES and opens one temporary dedicated connection per discovered endpoint, with at most eight nodes active at once. This limit applies per call; avoid overlapping polls when controlling total connection demand. These pools are scoped to the call and closed on every outcome, including success. They do not retain historical replicas or add transports to the routing pool cache. Client disposal tracks and aborts active fan-out pools, including pending handshakes. TCP/TLS setup uses ConnectTimeout; Redis handshake replies and commands use CommandTimeout and the configured response watchdog. Caller cancellation also covers nodes awaiting capacity. If response deadlines are disabled, supply a caller cancellation deadline to bound a stalled node's replies and the overall call.

Execution preserves authentication/TLS. A failed node produces an endpoint-associated error while other nodes can succeed. Cancellation before or during discovery throws. After discovery, cancellation is recorded in the affected node results; already completed successes remain available. No command is replayed or redirected away from its target endpoint.

Custom IServerCommands implementations, decorators, and mocks must implement or forward all six new methods. No deferred command facet is added.

Redis contracts: CHANNELS, NUMSUB, NUMPAT, SHARDCHANNELS, and SHARDNUMSUB.