Skip to content

Commit b4bb033

Browse files
committed
fix(NODE-5106): Add MongoClient connect lock mechanism to avoid topology leak
1 parent 900cc91 commit b4bb033

File tree

1 file changed

+43
-30
lines changed

1 file changed

+43
-30
lines changed

src/mongo_client.ts

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,8 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
318318
topology?: Topology;
319319
/** @internal */
320320
readonly mongoLogger: MongoLogger;
321+
/** @internal */
322+
private connectionLock?: Promise<this>;
321323

322324
/**
323325
* The consolidate, parsed, transformed and merged options.
@@ -409,46 +411,57 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
409411
return this;
410412
}
411413

412-
const options = this[kOptions];
414+
if (this.connectionLock) {
415+
return this.connectionLock;
416+
}
417+
418+
this.connectionLock = (async () => {
419+
const options = this[kOptions];
413420

414-
if (typeof options.srvHost === 'string') {
415-
const hosts = await resolveSRVRecord(options);
421+
if (typeof options.srvHost === 'string') {
422+
const hosts = await resolveSRVRecord(options);
416423

417-
for (const [index, host] of hosts.entries()) {
418-
options.hosts[index] = host;
424+
for (const [index, host] of hosts.entries()) {
425+
options.hosts[index] = host;
426+
}
419427
}
420-
}
421428

422-
const topology = new Topology(options.hosts, options);
423-
// Events can be emitted before initialization is complete so we have to
424-
// save the reference to the topology on the client ASAP if the event handlers need to access it
425-
this.topology = topology;
426-
topology.client = this;
429+
const topology = new Topology(options.hosts, options);
430+
// Events can be emitted before initialization is complete so we have to
431+
// save the reference to the topology on the client ASAP if the event handlers need to access it
432+
this.topology = topology;
433+
topology.client = this;
427434

428-
topology.once(Topology.OPEN, () => this.emit('open', this));
435+
topology.once(Topology.OPEN, () => this.emit('open', this));
429436

430-
for (const event of MONGO_CLIENT_EVENTS) {
431-
topology.on(event, (...args: any[]) => this.emit(event, ...(args as any)));
432-
}
437+
for (const event of MONGO_CLIENT_EVENTS) {
438+
topology.on(event, (...args: any[]) => this.emit(event, ...(args as any)));
439+
}
433440

434-
const topologyConnect = async () => {
435-
try {
436-
await promisify(callback => topology.connect(options, callback))();
437-
} catch (error) {
438-
topology.close({ force: true });
439-
throw error;
441+
const topologyConnect = async () => {
442+
try {
443+
await promisify(callback => topology.connect(options, callback))();
444+
} catch (error) {
445+
topology.close({ force: true });
446+
throw error;
447+
}
448+
};
449+
450+
if (this.autoEncrypter) {
451+
const initAutoEncrypter = promisify(callback => this.autoEncrypter?.init(callback));
452+
await initAutoEncrypter();
453+
await topologyConnect();
454+
await options.encrypter.connectInternalClient();
455+
} else {
456+
await topologyConnect();
440457
}
441-
};
442458

443-
if (this.autoEncrypter) {
444-
const initAutoEncrypter = promisify(callback => this.autoEncrypter?.init(callback));
445-
await initAutoEncrypter();
446-
await topologyConnect();
447-
await options.encrypter.connectInternalClient();
448-
} else {
449-
await topologyConnect();
450-
}
459+
return this;
460+
})();
451461

462+
await this.connectionLock;
463+
// release
464+
this.connectionLock = undefined;
452465
return this;
453466
}
454467

0 commit comments

Comments
 (0)