From 7451b2c61ad6f992af52ec3e304f4f2ffdda326f Mon Sep 17 00:00:00 2001 From: Leibale Date: Tue, 16 Apr 2024 15:04:29 -0400 Subject: [PATCH] feat: add support for `socketTimeout` in `Redis` --- lib/Redis.ts | 22 ++++++++++++++++++++++ lib/redis/RedisOptions.ts | 9 +++++++++ 2 files changed, 31 insertions(+) diff --git a/lib/Redis.ts b/lib/Redis.ts index ff7cf29b..74c63a79 100644 --- a/lib/Redis.ts +++ b/lib/Redis.ts @@ -106,6 +106,7 @@ class Redis extends Commander implements DataHandledable { private connectionEpoch = 0; private retryAttempts = 0; private manuallyClosing = false; + private socketTimeoutTimer: NodeJS.Timeout | undefined; // Prepare autopipelines structures private _autoPipelines = new Map(); @@ -523,6 +524,10 @@ class Redis extends Commander implements DataHandledable { if (Command.checkFlag("WILL_DISCONNECT", command.name)) { this.manuallyClosing = true; } + + if (this.options.socketTimeout !== undefined && this.socketTimeoutTimer === undefined) { + this.setSocketTimeout(); + } } if (command.name === "select" && isInt(command.args[0])) { @@ -537,6 +542,23 @@ class Redis extends Commander implements DataHandledable { return command.promise; } + private setSocketTimeout() { + this.socketTimeoutTimer = setTimeout(() => { + this.stream.destroy(new Error(`Socket timeout. Expecting data, but didn't receive any in ${this.options.socketTimeout}ms.`)); + this.socketTimeoutTimer = undefined; + }, this.options.socketTimeout); + + // this handler must run after the "data" handler in "DataHandler" + // so that `this.commandQueue.length` will be updated + this.stream.once("data", () => { + console.log('GOT DATA, CLEARING TIMER'); + clearTimeout(this.socketTimeoutTimer); + this.socketTimeoutTimer = undefined; + if (this.commandQueue.length === 0) return; + this.setSocketTimeout(); + }); + } + scanStream(options?: ScanStreamOptions) { return this.createScanStream("scan", { options }); } diff --git a/lib/redis/RedisOptions.ts b/lib/redis/RedisOptions.ts index e580d191..26cc90aa 100644 --- a/lib/redis/RedisOptions.ts +++ b/lib/redis/RedisOptions.ts @@ -14,6 +14,15 @@ export interface CommonRedisOptions extends CommanderOptions { * a "Command timed out" error will be thrown. */ commandTimeout?: number; + + /** + * If the socket does not receive data within a set number of milliseconds: + * 1. the socket is considered "dead" and will be destroyed + * 2. the client will reject any running commands (altought they might have been processed by the server) + * 3. the reconnect strategy will kick in (depending on the configuration) + */ + socketTimeout?: number; + /** * Enable/disable keep-alive functionality. * @link https://nodejs.org/api/net.html#socketsetkeepaliveenable-initialdelay