diff --git a/src/sdam/topology.ts b/src/sdam/topology.ts index 8ed4899f9b6..722bdb62384 100644 --- a/src/sdam/topology.ts +++ b/src/sdam/topology.ts @@ -494,7 +494,7 @@ export class Topology extends TypedEventEmitter { closeCheckedOutConnections() { for (const server of this.s.servers.values()) { - return server.closeCheckedOutConnections(); + server.closeCheckedOutConnections(); } } diff --git a/test/integration/connection-monitoring-and-pooling/connection_pool.test.ts b/test/integration/connection-monitoring-and-pooling/connection_pool.test.ts index d073b5a1125..c1cb3b0f499 100644 --- a/test/integration/connection-monitoring-and-pooling/connection_pool.test.ts +++ b/test/integration/connection-monitoring-and-pooling/connection_pool.test.ts @@ -72,7 +72,7 @@ describe('Connection Pool', function () { }); }); - const metadata: MongoDBMetadataUI = { requires: { mongodb: '>=4.4', topology: 'single' } }; + const metadata: MongoDBMetadataUI = { requires: { mongodb: '>=4.4' } }; describe('ConnectionCheckedInEvent', metadata, function () { let client: MongoClient; @@ -89,13 +89,13 @@ describe('Connection Pool', function () { configureFailPoint: 'failCommand', mode: 'alwaysOn', data: { - failCommands: ['insert'], + failCommands: ['find'], blockConnection: true, blockTimeMS: 500 } }); - client = this.configuration.newClient(); + client = this.configuration.newClient({}, {}); await client.connect(); await Promise.all(Array.from({ length: 100 }, () => client.db().command({ ping: 1 }))); }); @@ -120,37 +120,37 @@ describe('Connection Pool', function () { .on('connectionCheckedIn', pushToClientEvents) .on('connectionClosed', pushToClientEvents); - const inserts = Promise.allSettled([ - client.db('test').collection('test').insertOne({ a: 1 }), - client.db('test').collection('test').insertOne({ a: 1 }), - client.db('test').collection('test').insertOne({ a: 1 }) + const finds = Promise.allSettled([ + client.db('test').collection('test').findOne({ a: 1 }), + client.db('test').collection('test').findOne({ a: 1 }), + client.db('test').collection('test').findOne({ a: 1 }) ]); - // wait until all pings are pending on the server + // wait until all finds are pending on the server while (allClientEvents.filter(e => e.name === 'connectionCheckedOut').length < 3) { await sleep(1); } - const insertConnectionIds = allClientEvents + const findConnectionIds = allClientEvents .filter(e => e.name === 'connectionCheckedOut') .map(({ address, connectionId }) => `${address} + ${connectionId}`); await client.close(); - const insertCheckInAndCloses = allClientEvents + const findCheckInAndCloses = allClientEvents .filter(e => e.name === 'connectionCheckedIn' || e.name === 'connectionClosed') .filter(({ address, connectionId }) => - insertConnectionIds.includes(`${address} + ${connectionId}`) + findConnectionIds.includes(`${address} + ${connectionId}`) ); - expect(insertCheckInAndCloses).to.have.lengthOf(6); + expect(findCheckInAndCloses).to.have.lengthOf(6); // check that each check-in is followed by a close (not proceeded by one) - expect(insertCheckInAndCloses.map(e => e.name)).to.deep.equal( + expect(findCheckInAndCloses.map(e => e.name)).to.deep.equal( Array.from({ length: 3 }, () => ['connectionCheckedIn', 'connectionClosed']).flat(1) ); - await inserts; + await finds; } ); }); diff --git a/test/integration/node-specific/client_close.test.ts b/test/integration/node-specific/client_close.test.ts index 98053aa0afb..c5a20a75a09 100644 --- a/test/integration/node-specific/client_close.test.ts +++ b/test/integration/node-specific/client_close.test.ts @@ -10,7 +10,12 @@ import { type FindCursor, type MongoClient } from '../../mongodb'; -import { configureMongocryptdSpawnHooks } from '../../tools/utils'; +import { + clearFailPoint, + configureFailPoint, + configureMongocryptdSpawnHooks, + sleep +} from '../../tools/utils'; import { filterForCommands } from '../shared'; import { runScriptAndGetProcessInfo } from './resource_tracking_script_builder'; @@ -716,6 +721,78 @@ describe('MongoClient.close() Integration', () => { }); }); + describe('closeCheckedOutConnections', () => { + const metadata: MongoDBMetadataUI = { requires: { mongodb: '>=4.4' } }; + let client: MongoClient; + + beforeEach(async function () { + await configureFailPoint(this.configuration, { + configureFailPoint: 'failCommand', + mode: 'alwaysOn', + data: { + failCommands: ['find'], + blockConnection: true, + blockTimeMS: 500 + } + }); + client = this.configuration.newClient({}, {}); + await client.connect(); + }); + + afterEach(async function () { + await clearFailPoint(this.configuration); + await client?.close(); + }); + + it( + 'emits connectionCheckedIn immediately followed by connectionClosed for each in-flight connection', + metadata, + async function () { + const allEvents: Array<{ name: string; address: string; connectionId: number }> = []; + const push = e => allEvents.push(e); + + client + .on('connectionCheckedOut', push) + .on('connectionCheckedIn', push) + .on('connectionClosed', push); + + const finds = Promise.allSettled([ + client.db('test').collection('test').findOne({ a: 1 }), + client.db('test').collection('test').findOne({ a: 1 }), + client.db('test').collection('test').findOne({ a: 1 }) + ]); + + // wait until all three finds have checked out a connection + while (allEvents.filter(e => e.name === 'connectionCheckedOut').length < 3) { + await sleep(1); + } + + const findConnectionIds = new Set( + allEvents + .filter(e => e.name === 'connectionCheckedOut') + .map(({ address, connectionId }) => `${address}+${connectionId}`) + ); + + await client.close(); + + const findEvents = allEvents + .filter(e => e.name === 'connectionCheckedIn' || e.name === 'connectionClosed') + .filter(({ address, connectionId }) => + findConnectionIds.has(`${address}+${connectionId}`) + ); + + expect(findEvents).to.have.lengthOf(6); + + // spec requires each connectionCheckedIn to be immediately followed by connectionClosed + expect(findEvents.map(e => e.name)).to.deep.equal( + Array.from({ length: 3 }, () => ['connectionCheckedIn', 'connectionClosed']).flat() + ); + + await finds; + } + ); + }); + describe('Server resource: Cursor', () => { describe('after cursors are created', () => { let client: MongoClient;