1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-04 15:02:09 +03:00

enhance cluster reshard handling

This commit is contained in:
leibale
2021-11-22 17:42:41 -05:00
parent 6946e36ba0
commit 42e36dfbb1
4 changed files with 76 additions and 28 deletions

View File

@ -42,20 +42,8 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisSc
throw new Error('None of the root nodes is available');
}
async discover(startWith: RedisClientType<M, S>): Promise<void> {
if (await this.#discoverNodes(startWith.options)) return;
for (const { client } of this.#nodeByUrl.values()) {
if (client === startWith) continue;
if (await this.#discoverNodes(client.options)) return;
}
throw new Error('None of the cluster nodes is available');
}
async #discoverNodes(clientOptions?: RedisClusterClientOptions): Promise<boolean> {
const client = new this.#Client(clientOptions);
const client = this.#initiateClient(clientOptions);
await client.connect();
@ -72,6 +60,29 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisSc
}
}
#runningRediscoverPromise?: Promise<void>;
async rediscover(startWith: RedisClientType<M, S>): Promise<void> {
if (!this.#runningRediscoverPromise) {
this.#runningRediscoverPromise = this.#rediscover(startWith)
.finally(() => this.#runningRediscoverPromise = undefined);
}
return this.#runningRediscoverPromise;
}
async #rediscover(startWith: RedisClientType<M, S>): Promise<void> {
if (await this.#discoverNodes(startWith.options)) return;
for (const { client } of this.#nodeByUrl.values()) {
if (client === startWith) continue;
if (await this.#discoverNodes(client.options)) return;
}
throw new Error('None of the cluster nodes is available');
}
async #reset(masters: Array<RedisClusterMasterNode>): Promise<void> {
// Override this.#slots and add not existing clients to this.#nodeByUrl
const promises: Array<Promise<void>> = [],
@ -103,18 +114,23 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisSc
await Promise.all(promises);
}
#clientOptionsDefaults(options: RedisClusterClientOptions): RedisClusterClientOptions {
#clientOptionsDefaults(options?: RedisClusterClientOptions): RedisClusterClientOptions | undefined {
if (!this.#options.defaults) return options;
const merged = Object.assign({}, this.#options.defaults, options);
if (options.socket && this.#options.defaults.socket) {
if (options?.socket && this.#options.defaults.socket) {
Object.assign({}, this.#options.defaults.socket, options.socket);
}
return merged;
}
#initiateClient(options?: RedisClusterClientOptions): RedisClientType<M, S> {
return new this.#Client(this.#clientOptionsDefaults(options))
.on('error', this.#onError);
}
#initiateClientForNode(nodeData: RedisClusterMasterNode | RedisClusterReplicaNode, readonly: boolean, clientsInUse: Set<string>, promises: Array<Promise<void>>): ClusterNode<M, S> {
const url = `${nodeData.host}:${nodeData.port}`;
clientsInUse.add(url);
@ -123,15 +139,13 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisSc
if (!node) {
node = {
id: nodeData.id,
client: new this.#Client(
this.#clientOptionsDefaults({
socket: {
host: nodeData.host,
port: nodeData.port
},
readonly
})
)
client: this.#initiateClient({
socket: {
host: nodeData.host,
port: nodeData.port
},
readonly
})
};
promises.push(node.client.connect());
this.#nodeByUrl.set(url, node);

View File

@ -157,7 +157,7 @@ export default class RedisCluster<M extends RedisModules = Record<string, never>
const url = err.message.substring(err.message.lastIndexOf(' ') + 1);
let node = this.#slots.getNodeByUrl(url);
if (!node) {
await this.#slots.discover(client);
await this.#slots.rediscover(client);
node = this.#slots.getNodeByUrl(url);
if (!node) {
@ -168,7 +168,7 @@ export default class RedisCluster<M extends RedisModules = Record<string, never>
await node.client.asking();
return node.client;
} else if (err.message.startsWith('MOVED')) {
await this.#slots.discover(client);
await this.#slots.rediscover(client);
return true;
}