Skip to content

Commit dc920d3

Browse files
authored
fix isolationPool after reconnect (#2409)
* fix #2406 - fix isolationPool after reconnect * revert breaking change * fix
1 parent 986a510 commit dc920d3

File tree

2 files changed

+59
-20
lines changed

2 files changed

+59
-20
lines changed

packages/client/lib/client/index.spec.ts

+36-6
Original file line numberDiff line numberDiff line change
@@ -607,11 +607,41 @@ describe('Client', () => {
607607
}
608608
});
609609

610-
testUtils.testWithClient('executeIsolated', async client => {
611-
const id = await client.clientId(),
612-
isolatedId = await client.executeIsolated(isolatedClient => isolatedClient.clientId());
613-
assert.ok(id !== isolatedId);
614-
}, GLOBAL.SERVERS.OPEN);
610+
describe('isolationPool', () => {
611+
testUtils.testWithClient('executeIsolated', async client => {
612+
const id = await client.clientId(),
613+
isolatedId = await client.executeIsolated(isolatedClient => isolatedClient.clientId());
614+
assert.ok(id !== isolatedId);
615+
}, GLOBAL.SERVERS.OPEN);
616+
617+
testUtils.testWithClient('should be able to use pool even before connect', async client => {
618+
await client.executeIsolated(() => Promise.resolve());
619+
// make sure to destroy isolation pool
620+
await client.connect();
621+
await client.disconnect();
622+
}, {
623+
...GLOBAL.SERVERS.OPEN,
624+
disableClientSetup: true
625+
});
626+
627+
testUtils.testWithClient('should work after reconnect (#2406)', async client => {
628+
await client.disconnect();
629+
await client.connect();
630+
await client.executeIsolated(() => Promise.resolve());
631+
}, GLOBAL.SERVERS.OPEN);
632+
633+
testUtils.testWithClient('should throw ClientClosedError after disconnect', async client => {
634+
await client.connect();
635+
await client.disconnect();
636+
await assert.rejects(
637+
client.executeIsolated(() => Promise.resolve()),
638+
ClientClosedError
639+
);
640+
}, {
641+
...GLOBAL.SERVERS.OPEN,
642+
disableClientSetup: true
643+
});
644+
});
615645

616646
async function killClient<
617647
M extends RedisModules,
@@ -731,7 +761,7 @@ describe('Client', () => {
731761
members.map<MemberTuple>(member => [member.value, member.score]).sort(sort)
732762
);
733763
}, GLOBAL.SERVERS.OPEN);
734-
764+
735765
describe('PubSub', () => {
736766
testUtils.testWithClient('should be able to publish and subscribe to messages', async publisher => {
737767
function assertStringListener(message: string, channel: string) {

packages/client/lib/client/index.ts

+23-14
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '.
1515
import { URL } from 'url';
1616
import { TcpSocketConnectOpts } from 'net';
1717
import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub';
18-
import { callbackify } from 'util';
1918

2019
export interface RedisClientOptions<
2120
M extends RedisModules = RedisModules,
@@ -190,7 +189,7 @@ export default class RedisClient<
190189
readonly #options?: RedisClientOptions<M, F, S>;
191190
readonly #socket: RedisSocket;
192191
readonly #queue: RedisCommandsQueue;
193-
readonly #isolationPool: Pool<RedisClientType<M, F, S>>;
192+
#isolationPool?: Pool<RedisClientType<M, F, S>>;
194193
readonly #v4: Record<string, any> = {};
195194
#selectedDB = 0;
196195

@@ -223,16 +222,9 @@ export default class RedisClient<
223222
this.#options = this.#initiateOptions(options);
224223
this.#queue = this.#initiateQueue();
225224
this.#socket = this.#initiateSocket();
226-
this.#isolationPool = createPool({
227-
create: async () => {
228-
const duplicate = this.duplicate({
229-
isolationPoolOptions: undefined
230-
}).on('error', err => this.emit('error', err));
231-
await duplicate.connect();
232-
return duplicate;
233-
},
234-
destroy: client => client.disconnect()
235-
}, options?.isolationPoolOptions);
225+
// should be initiated in connect, not here
226+
// TODO: consider breaking in v5
227+
this.#isolationPool = this.#initiateIsolationPool();
236228
this.#legacyMode();
237229
}
238230

@@ -337,6 +329,19 @@ export default class RedisClient<
337329
.on('end', () => this.emit('end'));
338330
}
339331

332+
#initiateIsolationPool() {
333+
return createPool({
334+
create: async () => {
335+
const duplicate = this.duplicate({
336+
isolationPoolOptions: undefined
337+
}).on('error', err => this.emit('error', err));
338+
await duplicate.connect();
339+
return duplicate;
340+
},
341+
destroy: client => client.disconnect()
342+
}, this.#options?.isolationPoolOptions);
343+
}
344+
340345
#legacyMode(): void {
341346
if (!this.#options?.legacyMode) return;
342347

@@ -422,6 +427,8 @@ export default class RedisClient<
422427
}
423428

424429
connect(): Promise<void> {
430+
// see comment in constructor
431+
this.#isolationPool ??= this.#initiateIsolationPool();
425432
return this.#socket.connect();
426433
}
427434

@@ -704,6 +711,7 @@ export default class RedisClient<
704711
}
705712

706713
executeIsolated<T>(fn: (client: RedisClientType<M, F, S>) => T | Promise<T>): Promise<T> {
714+
if (!this.#isolationPool) return Promise.reject(new ClientClosedError());
707715
return this.#isolationPool.use(fn);
708716
}
709717

@@ -802,8 +810,9 @@ export default class RedisClient<
802810
}
803811

804812
async #destroyIsolationPool(): Promise<void> {
805-
await this.#isolationPool.drain();
806-
await this.#isolationPool.clear();
813+
await this.#isolationPool!.drain();
814+
await this.#isolationPool!.clear();
815+
this.#isolationPool = undefined;
807816
}
808817

809818
ref(): void {

0 commit comments

Comments
 (0)