Skip to content

Commit d185520

Browse files
committed
fix(NODE-5225): eagerly clear MongoClient.topology in MongoClient.close()
At the moment, calling `MongoClient.close()` in quick succession leads to an error: ``` TypeError: Cannot read properties of undefined (reading 'close') at node-mongodb-native/src/mongo_client.ts:530:16 at new Promise (<anonymous>) at LegacyMongoClient.close (src/mongo_client.ts:529:11) at processTicksAndRejections (node:internal/process/task_queues:95:5) at async Promise.all (index 1) at async Context.<anonymous> (test/integration/node-specific/mongo_client.test.ts:393:5) ``` This happens because: 1. The first call attempts to asynchronously [close][1] the session pool sessions 2. While this is happening, the second call passes the topology [null check][2] (since `this.topology` hasn't yet been [unset][3], and tries to also close session pool sessions 3. Both calls then set [`const topology = this.topology`][4] but the second call will get undefined since the first call [unset][3] `this.topology` 4. The second call now tries to call [`close()`][5] on an `undefined` `topology` and throws This change fixes the issue by moving the `topology` unset immediately after the `null` check, so we know it's always correctly set. [1]: https://github.com/mongodb/node-mongodb-native/blob/325c4bc37decdf12e957bfad8bd4ee4d28b1bf95/src/mongo_client.ts#L516 [2]: https://github.com/mongodb/node-mongodb-native/blob/325c4bc37decdf12e957bfad8bd4ee4d28b1bf95/src/mongo_client.ts#L503 [3]: https://github.com/mongodb/node-mongodb-native/blob/325c4bc37decdf12e957bfad8bd4ee4d28b1bf95/src/mongo_client.ts#L527 [4]: https://github.com/mongodb/node-mongodb-native/blob/325c4bc37decdf12e957bfad8bd4ee4d28b1bf95/src/mongo_client.ts#L526 [5]: https://github.com/mongodb/node-mongodb-native/blob/325c4bc37decdf12e957bfad8bd4ee4d28b1bf95/src/mongo_client.ts#L530
1 parent 325c4bc commit d185520

File tree

2 files changed

+13
-5
lines changed

2 files changed

+13
-5
lines changed

src/mongo_client.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -504,10 +504,14 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
504504
return;
505505
}
506506

507+
// clear out references to old topology
508+
const topology = this.topology;
509+
this.topology = undefined;
510+
507511
// If we would attempt to select a server and get nothing back we short circuit
508512
// to avoid the server selection timeout.
509513
const selector = readPreferenceServerSelector(ReadPreference.primaryPreferred);
510-
const topologyDescription = this.topology.description;
514+
const topologyDescription = topology.description;
511515
const serverDescriptions = Array.from(topologyDescription.servers.values());
512516
const servers = selector(topologyDescription, serverDescriptions);
513517
if (servers.length !== 0) {
@@ -522,10 +526,6 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
522526
}
523527
}
524528

525-
// clear out references to old topology
526-
const topology = this.topology;
527-
this.topology = undefined;
528-
529529
await new Promise<void>((resolve, reject) => {
530530
topology.close({ force }, error => {
531531
if (error) return reject(error);

test/integration/node-specific/mongo_client.test.ts

+8
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,14 @@ describe('class MongoClient', function () {
385385
}
386386
});
387387

388+
it('it can call close() concurrently', async function () {
389+
const client = this.configuration.newClient();
390+
await client.connect();
391+
// Ensure topology is opened before trying to close
392+
await client.db().command({ hello: 1 });
393+
await Promise.all([client.close(), client.close()]);
394+
});
395+
388396
context('explict #connect()', () => {
389397
let client: MongoClient;
390398
beforeEach(function () {

0 commit comments

Comments
 (0)