Skip to content

Commit 942de1f

Browse files
authored
Handle unhandled errors in socket.reconnectStrategry (#2226)
* handle errors in reconnect strategy * add test * fix retries typo * fix #2237 - flush queues on reconnect strategy error * Update socket.ts * Update socket.ts
1 parent 1fdee05 commit 942de1f

File tree

2 files changed

+59
-24
lines changed

2 files changed

+59
-24
lines changed
+39-17
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,63 @@
11
import { strict as assert } from 'assert';
22
import { SinonFakeTimers, useFakeTimers, spy } from 'sinon';
3-
import RedisSocket from './socket';
3+
import RedisSocket, { RedisSocketOptions } from './socket';
44

55
describe('Socket', () => {
6+
function createSocket(options: RedisSocketOptions): RedisSocket {
7+
const socket = new RedisSocket(
8+
() => Promise.resolve(),
9+
options
10+
);
11+
12+
socket.on('error', (err) => {
13+
// ignore errors
14+
console.log(err);
15+
});
16+
17+
return socket;
18+
}
19+
620
describe('reconnectStrategy', () => {
721
let clock: SinonFakeTimers;
822
beforeEach(() => clock = useFakeTimers());
923
afterEach(() => clock.restore());
1024

11-
it('custom strategy', () => {
12-
const reconnectStrategy = spy((retries: number): number | Error => {
25+
it('custom strategy', async () => {
26+
const reconnectStrategy = spy((retries: number) => {
1327
assert.equal(retries + 1, reconnectStrategy.callCount);
1428

15-
if (retries === 50) {
16-
return Error('50');
17-
}
29+
if (retries === 50) return new Error('50');
1830

1931
const time = retries * 2;
2032
queueMicrotask(() => clock.tick(time));
2133
return time;
2234
});
2335

24-
const socket = new RedisSocket(
25-
() => Promise.resolve(),
26-
{
27-
host: 'error',
28-
reconnectStrategy
29-
}
30-
);
31-
32-
socket.on('error', () => {
33-
// ignore errors
36+
const socket = createSocket({
37+
host: 'error',
38+
reconnectStrategy
3439
});
3540

36-
return assert.rejects(socket.connect(), {
41+
await assert.rejects(socket.connect(), {
3742
message: '50'
3843
});
44+
45+
assert.equal(socket.isOpen, false);
46+
});
47+
48+
it('should handle errors', async () => {
49+
const socket = createSocket({
50+
host: 'error',
51+
reconnectStrategy(retries: number) {
52+
if (retries === 1) return new Error('done');
53+
queueMicrotask(() => clock.tick(500));
54+
throw new Error();
55+
}
56+
});
57+
58+
await assert.rejects(socket.connect());
59+
60+
assert.equal(socket.isOpen, false);
3961
});
4062
});
4163
});

packages/client/lib/client/socket.ts

+20-7
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ export default class RedisSocket extends EventEmitter {
4444
return options;
4545
}
4646

47-
static #defaultReconnectStrategy(retries: number): number {
48-
return Math.min(retries * 50, 500);
49-
}
50-
5147
static #isTlsSocket(options: RedisSocketOptions): options is RedisTlsSocketOptions {
5248
return (options as RedisTlsSocketOptions).tls === true;
5349
}
@@ -87,6 +83,23 @@ export default class RedisSocket extends EventEmitter {
8783
this.#options = RedisSocket.#initiateOptions(options);
8884
}
8985

86+
reconnectStrategy(retries: number): number | Error {
87+
if (this.#options.reconnectStrategy) {
88+
try {
89+
const retryIn = this.#options.reconnectStrategy(retries);
90+
if (typeof retryIn !== 'number' && !(retryIn instanceof Error)) {
91+
throw new TypeError('Reconnect strategy should return `number | Error`');
92+
}
93+
94+
return retryIn;
95+
} catch (err) {
96+
this.emit('error', err);
97+
}
98+
}
99+
100+
return Math.min(retries * 50, 500);
101+
}
102+
90103
async connect(): Promise<void> {
91104
if (this.#isOpen) {
92105
throw new Error('Socket already opened');
@@ -116,14 +129,14 @@ export default class RedisSocket extends EventEmitter {
116129
this.#isReady = true;
117130
this.emit('ready');
118131
} catch (err) {
119-
this.emit('error', err);
120-
121-
const retryIn = (this.#options?.reconnectStrategy ?? RedisSocket.#defaultReconnectStrategy)(retries);
132+
const retryIn = this.reconnectStrategy(retries);
122133
if (retryIn instanceof Error) {
123134
this.#isOpen = false;
135+
this.emit('error', err);
124136
throw new ReconnectStrategyError(retryIn, err);
125137
}
126138

139+
this.emit('error', err);
127140
await promiseTimeout(retryIn);
128141
return this.#connect(retries + 1);
129142
}

0 commit comments

Comments
 (0)