From dbb0d9e392c6c405699991f208f7714e98a81da6 Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 13 Dec 2023 21:19:27 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20improve=20redirects=20lo?= =?UTF-8?q?gic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cluster/RedisCluster.ts | 25 +++++++++++++++++-------- src/cluster/RedisClusterCall.ts | 13 +++++++++++-- src/demo-cluster.ts | 5 ++++- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/cluster/RedisCluster.ts b/src/cluster/RedisCluster.ts index 80848aa..d86251d 100644 --- a/src/cluster/RedisCluster.ts +++ b/src/cluster/RedisCluster.ts @@ -60,7 +60,6 @@ export class RedisCluster { await this.router.rebuild(client); if (this.stopped) return; this.initialTableBuildAttempt = 0; - console.log('SHARD TABLE CREATED'); this.onRouter.emit(); })().catch((error) => { const delay = Math.max(Math.min(1000 * 2 ** attempt, 1000 * 60), 1000); @@ -108,15 +107,18 @@ export class RedisCluster { } private async createClientFromInfo(info: RedisClusterNodeInfo): Promise { - const [client] = await this.createClient({ + return this.createClientForHost(info.endpoint || info.ip, info.port); + } + + private async createClientForHost(host: string, port: number): Promise { + return this.createClient({ ...this.opts.connectionConfig, - host: info.endpoint || info.ip, - port: info.port, + host, + port, }); - return client; } - protected async createClient(config: RedisClusterNodeClientOpts): Promise<[client: RedisClusterNodeClient, id: string]> { + protected async createClient(config: RedisClusterNodeClientOpts): Promise { const client = this.createClientRaw(config); client.start(); const {user, pwd} = config; @@ -124,8 +126,9 @@ export class RedisCluster { client.hello(3, pwd, user), client.clusterMyId(), ]); + client.id = id; this.router.setClient(id, client); - return [client, id]; + return client; } protected createClientRaw(config: RedisClusterNodeClientOpts): RedisClusterNodeClient { @@ -154,7 +157,13 @@ export class RedisCluster { const redirect = parseMovedError((error as Error).message); let host = redirect[0] || client.host; if (!host) throw new Error('NO_HOST'); - // TODO: Start router table rebuild. + call.redirects++; + if (call.redirects > call.maxRedirects) throw new Error('MAX_REDIRECTS'); + const port = redirect[1]; + console.log('redirect', host, port, call.redirects); + if (host === client.host && port === client.port) throw new Error('INVALID_REDIRECT'); + const nextClient = await this.createClientForHost(host, port); + return this.callWithClient(call, nextClient); } throw error; } diff --git a/src/cluster/RedisClusterCall.ts b/src/cluster/RedisClusterCall.ts index 3b16e60..f066dcc 100644 --- a/src/cluster/RedisClusterCall.ts +++ b/src/cluster/RedisClusterCall.ts @@ -5,8 +5,17 @@ import {RedisCall} from "../node"; */ export class RedisClusterCall extends RedisCall { /** - * Key to use for routing the command to the correct node. Used in cluster - * mode. + * Key to use for routing the command to the correct node. */ public key: string = ''; + + /** + * Number of redirects that have been performed for this command. + */ + public redirects: number = 0; + + /** + * Maximum number of redirects to perform before giving up. + */ + public maxRedirects: number = 4; } diff --git a/src/demo-cluster.ts b/src/demo-cluster.ts index bdd8b14..451625b 100644 --- a/src/demo-cluster.ts +++ b/src/demo-cluster.ts @@ -3,7 +3,9 @@ import {RedisCluster} from "./cluster/RedisCluster"; const main = async () => { - const host = 'localhost'; + // const host = 'localhost'; + const host = '127.0.0.1'; + // const host = '172.17.0.2'; const port = 7000; const user = 'default'; const pwd = 'AoQhB7bNYljT8IiZ7nbgvSQSXiGHRwQX'; @@ -37,6 +39,7 @@ const main = async () => { }; await exec(['SET', 'foo', 'bar']); + // await exec(['GET', 'foo']); }; main().catch((err) => {