socketio/socket.io-redis-adapter

In shard mode, close throw error "CROSSSLOT Keys in request don't hash to the same slot"

ngot opened this issue · 3 comments

ngot commented

I had a chance to try the new shard PubSub adapter. Mostly works good, but when enable cleanupEmptyChildNamespaces, the close function will throw "CROSSSLOT Keys in request don't hash to the same slot". The root cause is redis/node-redis#2502

Currently,

    close() {
        const channels = [this.channel, this.responseChannel];
        if (this.opts.subscriptionMode === "dynamic") {
            this.rooms.forEach((_sids, room) => {
                const isPublicRoom = !this.sids.has(room);
                if (isPublicRoom) {
                    channels.push(this.dynamicChannel(room));
                }
            });
        }
       // channels has at least 2 different strings, `sUnsubscribe` assumes 
       // they would be hashed to the same slot which is not correct.
       return this.subClient.sUnsubscribe(channels);
    }

A potential workaround

return Promise.all(channels.map((channel) => this.subClient.sUnsubscribe(channel)));
// return this.subClient.sUnsubscribe(channels);

Hi! Thanks for raising the issue, I'm digging into this.

ngot commented

According to redis/node-redis#2502 (comment), the sUnsubscribe(Array[]) assumes all the channels use "hash tags", so they are allocated to the same slot.

In socket.io-redis-adapter, there is dynamic channels, so sUnsubscribe one by one looks more doable.

Additionally, this is the usage of iosredis, it does not support array, but spread parameters.
https://github.com/luin/ioredis/blob/a3838ae7598c7d9d3aff688923403f6176d7a393/lib/utils/RedisCommander.ts#L8356

  /**
   * Stop listening for messages posted to the given shard channels
   * - _group_: pubsub
   * - _complexity_: O(N) where N is the number of clients already subscribed to a shard channel.
   * - _since_: 7.0.0
   */
  sunsubscribe(callback?: Callback<unknown>): Result<unknown, Context>;
  sunsubscribe(
    ...args: [
      ...shardchannels: (string | Buffer)[],
      callback: Callback<unknown>
    ]
  ): Result<unknown, Context>;
  sunsubscribe(
    ...args: [...shardchannels: (string | Buffer)[]]
  ): Result<unknown, Context>;

This should be fixed by 2da8d9e, included in version 8.2.1.

@ngot thanks for the analysis 👍