Sharded cluster pub/sub resubscribe fails with CROSSSLOT error

Description

When a pub/sub client reconnects to a host after an error or a socket disconnect, it attempts to resubscribe to previously subscribed channels:

resubscribe() {
const commands = [];
for (const [type, listeners] of Object.entries(this.listeners)) {
if (!listeners.size) continue;
this.#isActive = true;
this.#subscribing++;
const callback = () => this.#subscribing--;
commands.push({
args: [
COMMANDS[type as PubSubType].subscribe,
...listeners.keys()
],
channelsCounter: listeners.size,
resolve: callback,
reject: callback
} satisfies PubSubCommand);
}
return commands;
}

It does this by issuing a single subscribe command with all listener channels as arguments. However, this is not valid with sharded pub/sub if the channels do not hash to the same slot, even if they are all owned by the same shard; the server will return a CROSSSLOT error (CROSSSLOT Keys in request don't hash to the same slot).

A simple solution for this might be to issue one subscribe command per channel for PubSubType.SHARDED subscriptions. However, there are some further issues with the current resubscription approach; for example, if a slot has moved between shards between the disconnect and the reconnect, the current logic won't be able to resubscribe correctly.

I'd be happy to open a PR implementing the rough solution (one command per channel) without consideration for moved slots, but would prefer if we can figure out a general solution that can handle MOVED errors as well!

Node.js Version

v23.6.1

Redis Server Version

7.4.2

Node Redis Version

4.7.0

Platform

Linux

Logs