Skip to content

Commit d65f8b2

Browse files
JosePedroDiasSkyluin
andauthoredApr 24, 2021
fix: clears commandTimeout timer as each respective command gets fulfilled (#1336)
* making commandTimeout clean successful timers and work in cluster mode * deleting timer attribution solves an issue with transactions; cluster command doesn't need any special behaviour after all * adding attribute annotation to keep typescript happy. * thought setTimeout returned a number, apparently not * this time I ran `npm run build` locally first * addressing feedback * Clear timeouts for commands in offline queue When client is not ready, Redis#sendCommand() will move commands to the offline queue, and once ready, all commands in the offline queue will be sent with Redis#sendCommand() again. So we should check whether the timer has been set. * Address feedbacks Co-authored-by: luin <i@zihua.li>
1 parent 9e140f0 commit d65f8b2

File tree

4 files changed

+36
-7
lines changed

4 files changed

+36
-7
lines changed
 

‎lib/command.ts

+21
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export default class Command implements ICommand {
156156
public isCustomCommand = false;
157157
public inTransaction = false;
158158
public pipelineIndex?: number;
159+
private _commandTimeoutTimer?: NodeJS.Timeout;
159160

160161
private slot?: number | null;
161162
private keys?: Array<string | Buffer>;
@@ -342,6 +343,12 @@ export default class Command implements ICommand {
342343
private _convertValue(resolve: Function): (result: any) => void {
343344
return (value) => {
344345
try {
346+
const existingTimer = this._commandTimeoutTimer;
347+
if (existingTimer) {
348+
clearTimeout(existingTimer);
349+
delete this._commandTimeoutTimer;
350+
}
351+
345352
resolve(this.transformReply(value));
346353
this.isResolved = true;
347354
} catch (err) {
@@ -370,6 +377,20 @@ export default class Command implements ICommand {
370377

371378
return result;
372379
}
380+
381+
/**
382+
* Set the wait time before terminating the attempt to execute a command
383+
* and generating an error.
384+
*/
385+
public setTimeout(ms: number) {
386+
if (!this._commandTimeoutTimer) {
387+
this._commandTimeoutTimer = setTimeout(() => {
388+
if (!this.isResolved) {
389+
this.reject(new Error("Command timed out"));
390+
}
391+
}, ms);
392+
}
393+
}
373394
}
374395

375396
const msetArgumentTransformer = function (args) {

‎lib/redis/index.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,7 @@ addTransactionSupport(Redis.prototype);
693693
* ```
694694
* @private
695695
*/
696-
Redis.prototype.sendCommand = function (command, stream) {
696+
Redis.prototype.sendCommand = function (command: Command, stream) {
697697
if (this.status === "wait") {
698698
this.connect().catch(noop);
699699
}
@@ -714,11 +714,7 @@ Redis.prototype.sendCommand = function (command, stream) {
714714
}
715715

716716
if (typeof this.options.commandTimeout === "number") {
717-
setTimeout(() => {
718-
if (!command.isResolved) {
719-
command.reject(new Error("Command timed out"));
720-
}
721-
}, this.options.commandTimeout);
717+
command.setTimeout(this.options.commandTimeout);
722718
}
723719

724720
if (command.name === "quit") {

‎lib/utils/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export function wrapMultiResult(arr) {
111111
* ```
112112
* @private
113113
*/
114-
export function isInt(value) {
114+
export function isInt(value): value is string {
115115
const x = parseFloat(value);
116116
return !isNaN(value) && (x | 0) === x;
117117
}

‎test/functional/commandTimeout.ts

+12
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,16 @@ describe("commandTimeout", function () {
2222
});
2323
clock.tick(1000);
2424
});
25+
26+
it("does not leak timers for commands in offline queue", async function () {
27+
const server = new MockServer(30001);
28+
29+
const redis = new Redis({ port: 30001, commandTimeout: 1000 });
30+
const clock = sinon.useFakeTimers();
31+
await redis.hget("foo");
32+
expect(clock.countTimers()).to.eql(0);
33+
clock.restore();
34+
redis.disconnect();
35+
await server.disconnectPromise();
36+
});
2537
});

0 commit comments

Comments
 (0)
Please sign in to comment.