You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-09 00:22:08 +03:00
fix(client): Avoids infinite promise-chaining when socket's creation fails (#2295)
* fix(client): timeout issues during tests * fix(client): avoiding infinite Promise chaining while socket creation fails * fix(client): Added missing semicolons * clean test Co-authored-by: leibale <leibale1998@gmail.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { strict as assert } from 'assert';
|
import { strict as assert } from 'assert';
|
||||||
import { SinonFakeTimers, useFakeTimers, spy } from 'sinon';
|
import { spy } from 'sinon';
|
||||||
import RedisSocket, { RedisSocketOptions } from './socket';
|
import RedisSocket, { RedisSocketOptions } from './socket';
|
||||||
|
|
||||||
describe('Socket', () => {
|
describe('Socket', () => {
|
||||||
@@ -9,37 +9,34 @@ describe('Socket', () => {
|
|||||||
options
|
options
|
||||||
);
|
);
|
||||||
|
|
||||||
socket.on('error', (err) => {
|
socket.on('error', () => {
|
||||||
// ignore errors
|
// ignore errors
|
||||||
console.log(err);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('reconnectStrategy', () => {
|
describe('reconnectStrategy', () => {
|
||||||
let clock: SinonFakeTimers;
|
|
||||||
beforeEach(() => clock = useFakeTimers());
|
|
||||||
afterEach(() => clock.restore());
|
|
||||||
|
|
||||||
it('custom strategy', async () => {
|
it('custom strategy', async () => {
|
||||||
|
const numberOfRetries = 10;
|
||||||
|
|
||||||
const reconnectStrategy = spy((retries: number) => {
|
const reconnectStrategy = spy((retries: number) => {
|
||||||
assert.equal(retries + 1, reconnectStrategy.callCount);
|
assert.equal(retries + 1, reconnectStrategy.callCount);
|
||||||
|
|
||||||
if (retries === 50) return new Error('50');
|
if (retries === numberOfRetries) return new Error(`${numberOfRetries}`);
|
||||||
|
|
||||||
const time = retries * 2;
|
const time = retries * 2;
|
||||||
queueMicrotask(() => clock.tick(time));
|
|
||||||
return time;
|
return time;
|
||||||
});
|
});
|
||||||
|
|
||||||
const socket = createSocket({
|
const socket = createSocket({
|
||||||
host: 'error',
|
host: 'error',
|
||||||
|
connectTimeout: 1,
|
||||||
reconnectStrategy
|
reconnectStrategy
|
||||||
});
|
});
|
||||||
|
|
||||||
await assert.rejects(socket.connect(), {
|
await assert.rejects(socket.connect(), {
|
||||||
message: '50'
|
message: `${numberOfRetries}`
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(socket.isOpen, false);
|
assert.equal(socket.isOpen, false);
|
||||||
@@ -48,9 +45,9 @@ describe('Socket', () => {
|
|||||||
it('should handle errors', async () => {
|
it('should handle errors', async () => {
|
||||||
const socket = createSocket({
|
const socket = createSocket({
|
||||||
host: 'error',
|
host: 'error',
|
||||||
|
connectTimeout: 1,
|
||||||
reconnectStrategy(retries: number) {
|
reconnectStrategy(retries: number) {
|
||||||
if (retries === 1) return new Error('done');
|
if (retries === 1) return new Error('done');
|
||||||
queueMicrotask(() => clock.tick(500));
|
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -105,10 +105,12 @@ export default class RedisSocket extends EventEmitter {
|
|||||||
throw new Error('Socket already opened');
|
throw new Error('Socket already opened');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.#connect(0);
|
return this.#connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
async #connect(retries: number, hadError?: boolean): Promise<void> {
|
async #connect(hadError?: boolean): Promise<void> {
|
||||||
|
let retries = 0;
|
||||||
|
do {
|
||||||
if (retries > 0 || hadError) {
|
if (retries > 0 || hadError) {
|
||||||
this.emit('reconnecting');
|
this.emit('reconnecting');
|
||||||
}
|
}
|
||||||
@@ -138,8 +140,9 @@ export default class RedisSocket extends EventEmitter {
|
|||||||
|
|
||||||
this.emit('error', err);
|
this.emit('error', err);
|
||||||
await promiseTimeout(retryIn);
|
await promiseTimeout(retryIn);
|
||||||
return this.#connect(retries + 1);
|
|
||||||
}
|
}
|
||||||
|
retries++;
|
||||||
|
} while (!this.#isReady);
|
||||||
}
|
}
|
||||||
|
|
||||||
#createSocket(): Promise<net.Socket | tls.TLSSocket> {
|
#createSocket(): Promise<net.Socket | tls.TLSSocket> {
|
||||||
@@ -200,7 +203,7 @@ export default class RedisSocket extends EventEmitter {
|
|||||||
this.#isReady = false;
|
this.#isReady = false;
|
||||||
this.emit('error', err);
|
this.emit('error', err);
|
||||||
|
|
||||||
this.#connect(0, true).catch(() => {
|
this.#connect(true).catch(() => {
|
||||||
// the error was already emitted, silently ignore it
|
// the error was already emitted, silently ignore it
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user