You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-12-11 09:22:35 +03:00
fix(ssubscribe): properly resubscribe in case of shard failover (#3098)
* fix(ssubscribe): properly resubscribe in case of shard failover 1) when RE failover happens, there is a disconnect 2) affected Client reconnects and tries to resubscribe all existing listeners ISSUE #1: CROSSSLOT Error - client was doing ssubscribe ch1 ch2.. chN which, after the failover could result in CROSSSLOT ( naturally, becasuse now some slots could be owned by other shards ) FIX: send one ssubscribe command per channel instead of one ssubscribe for all channels ISSUE #2: MOVED Error - some/all of the channels might be moved somewhere else FIX: 1: Propagate the error to the Cluster. 2: Cluster rediscovers topology. 3: Cluster resubscribes all listeners of the failed client ( possibly some/all of those will end up in a different client after the rediscovery ) fixes: #2902
This commit is contained in:
committed by
GitHub
parent
bd11e382d0
commit
96d6445d66
@@ -6,7 +6,7 @@ import { EventEmitter } from 'node:events';
|
||||
import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
|
||||
import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots';
|
||||
import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command';
|
||||
import { PubSubListener } from '../client/pub-sub';
|
||||
import { PubSubListener, PubSubListeners } from '../client/pub-sub';
|
||||
import { ErrorReply } from '../errors';
|
||||
import { RedisTcpSocketOptions } from '../client/socket';
|
||||
import { ClientSideCacheConfig, PooledClientSideCacheProvider } from '../client/cache';
|
||||
@@ -310,6 +310,7 @@ export default class RedisCluster<
|
||||
|
||||
this._options = options;
|
||||
this._slots = new RedisClusterSlots(options, this.emit.bind(this));
|
||||
this.on('__resubscribeAllPubSubListeners', this.resubscribeAllPubSubListeners.bind(this));
|
||||
|
||||
if (options?.commandOptions) {
|
||||
this._commandOptions = options.commandOptions;
|
||||
@@ -584,6 +585,33 @@ export default class RedisCluster<
|
||||
);
|
||||
}
|
||||
|
||||
resubscribeAllPubSubListeners(allListeners: PubSubListeners) {
|
||||
for(const [channel, listeners] of allListeners.CHANNELS) {
|
||||
listeners.buffers.forEach(bufListener => {
|
||||
this.subscribe(channel, bufListener, true);
|
||||
});
|
||||
listeners.strings.forEach(strListener => {
|
||||
this.subscribe(channel, strListener);
|
||||
});
|
||||
};
|
||||
for (const [channel, listeners] of allListeners.PATTERNS) {
|
||||
listeners.buffers.forEach(bufListener => {
|
||||
this.pSubscribe(channel, bufListener, true);
|
||||
});
|
||||
listeners.strings.forEach(strListener => {
|
||||
this.pSubscribe(channel, strListener);
|
||||
});
|
||||
};
|
||||
for (const [channel, listeners] of allListeners.SHARDED) {
|
||||
listeners.buffers.forEach(bufListener => {
|
||||
this.sSubscribe(channel, bufListener, true);
|
||||
});
|
||||
listeners.strings.forEach(strListener => {
|
||||
this.sSubscribe(channel, strListener);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
sUnsubscribe = this.SUNSUBSCRIBE;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user