diff --git a/src/cmap/connection_pool.ts b/src/cmap/connection_pool.ts index 00321ec234..976e280aae 100644 --- a/src/cmap/connection_pool.ts +++ b/src/cmap/connection_pool.ts @@ -691,7 +691,7 @@ export class ConnectionPool extends TypedEventEmitter { private ensureMinPoolSize() { const minPoolSize = this.options.minPoolSize; - if (this.poolState !== PoolState.ready || minPoolSize === 0) { + if (this.poolState !== PoolState.ready) { return; } 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 a765f42afe..d073b5a112 100644 --- a/test/integration/connection-monitoring-and-pooling/connection_pool.test.ts +++ b/test/integration/connection-monitoring-and-pooling/connection_pool.test.ts @@ -1,8 +1,14 @@ import { once } from 'node:events'; import { expect } from 'chai'; - -import { type ConnectionPoolCreatedEvent, type Db, type MongoClient } from '../../mongodb'; +import * as sinon from 'sinon'; + +import { + type ConnectionPoolCreatedEvent, + type Db, + type MongoClient, + type Server +} from '../../mongodb'; import { clearFailPoint, configureFailPoint, sleep } from '../../tools/utils'; describe('Connection Pool', function () { @@ -150,4 +156,51 @@ describe('Connection Pool', function () { }); }); }); + + describe( + 'background task cleans up connections when minPoolSize=0', + { requires: { topology: 'single' } }, + function () { + let server: Server; + let ensureMinPoolSizeSpy: sinon.SinonSpy; + + beforeEach(async function () { + client = this.configuration.newClient( + {}, + { + maxConnecting: 10, + minPoolSize: 0, + maxIdleTimeMS: 100 + } + ); + + await client.connect(); + + await Promise.all( + Array.from({ length: 10 }).map(() => { + return client.db('foo').collection('bar').insertOne({ a: 1 }); + }) + ); + + server = Array.from(client.topology.s.servers.entries())[0][1]; + expect( + server.pool.availableConnectionCount, + 'pool was not filled with connections' + ).to.be.greaterThan(0); + + ensureMinPoolSizeSpy = sinon.spy(server.pool, 'ensureMinPoolSize'); + }); + + it( + 'prunes idle connections when minPoolSize=0', + { requires: { topology: 'single' } }, + async function () { + await sleep(500); + expect(server.pool.availableConnectionCount).to.equal(0); + + expect(ensureMinPoolSizeSpy).to.have.been.called; + } + ); + } + ); }); diff --git a/test/unit/assorted/max_staleness.spec.test.js b/test/unit/assorted/max_staleness.spec.test.js index 2cdfbdc368..f0355cb1cc 100644 --- a/test/unit/assorted/max_staleness.spec.test.js +++ b/test/unit/assorted/max_staleness.spec.test.js @@ -45,13 +45,12 @@ describe('Max Staleness (spec)', function () { }); const specTests = collectStalenessTests(maxStalenessDir); - Object.keys(specTests).forEach(specTestName => { + for (const [specTestName, test] of Object.entries(specTests)) { describe(specTestName, () => { - specTests[specTestName].forEach(testData => { + for (const testData of test) it(testData.description, async function () { return executeServerSelectionTest(testData); }); - }); }); - }); + } }); diff --git a/test/unit/assorted/server_selection_latency_window_utils.ts b/test/unit/assorted/server_selection_latency_window_utils.ts index 8b6fd7e66c..9aa8dd2481 100644 --- a/test/unit/assorted/server_selection_latency_window_utils.ts +++ b/test/unit/assorted/server_selection_latency_window_utils.ts @@ -145,4 +145,6 @@ export async function runServerSelectionLatencyWindowTest(test: ServerSelectionL const observedFrequencies = calculateObservedFrequencies(selectedServers); compareResultsToExpected(test.outcome, observedFrequencies); + + await topology.close(); } diff --git a/test/unit/assorted/server_selection_spec_helper.js b/test/unit/assorted/server_selection_spec_helper.js index 8fb4bee676..20bcea87a2 100644 --- a/test/unit/assorted/server_selection_spec_helper.js +++ b/test/unit/assorted/server_selection_spec_helper.js @@ -123,7 +123,7 @@ async function executeServerSelectionTest(testDefinition) { const readPreference = readPreferenceFromDefinition(testDefinition.read_preference); selector = ServerSelectors.readPreferenceServerSelector(readPreference); } catch (e) { - if (testDefinition.error) return; + if (testDefinition.error) return topology.close(); throw e; } } else { @@ -179,6 +179,8 @@ async function executeServerSelectionTest(testDefinition) { // this is another expected error case if (expectedServers.length === 0 && err instanceof MongoServerSelectionError) return; throw err; + } finally { + topology.close(); } } diff --git a/test/unit/cmap/connection_pool.test.js b/test/unit/cmap/connection_pool.test.js index 1604cd82d8..61fa73fc0e 100644 --- a/test/unit/cmap/connection_pool.test.js +++ b/test/unit/cmap/connection_pool.test.js @@ -15,6 +15,7 @@ const { TimeoutContext } = require('../../mongodb'); describe('Connection Pool', function () { let timeoutContext; let mockMongod; + let pool; const stubServer = { topology: { client: { @@ -50,6 +51,8 @@ describe('Connection Pool', function () { timeoutContext = TimeoutContext.create({ waitQueueTimeoutMS: 0, serverSelectionTimeoutMS: 0 }); }); + afterEach(() => pool?.close()); + it('should destroy connections which have been closed', async function () { mockMongod.setMessageHandler(request => { const doc = request.document; @@ -61,7 +64,7 @@ describe('Connection Pool', function () { } }); - const pool = new ConnectionPool(stubServer, { + pool = new ConnectionPool(stubServer, { maxPoolSize: 1, hostAddress: mockMongod.hostAddress() }); @@ -81,6 +84,8 @@ describe('Connection Pool', function () { expect(events).to.have.length(1); const closeEvent = events[0]; expect(closeEvent).have.property('reason').equal('error'); + + conn.destroy(); }); it('should propagate socket timeouts to connections', async function () { @@ -93,7 +98,7 @@ describe('Connection Pool', function () { } }); - const pool = new ConnectionPool(stubServer, { + pool = new ConnectionPool(stubServer, { maxPoolSize: 1, socketTimeoutMS: 200, hostAddress: mockMongod.hostAddress() @@ -117,7 +122,7 @@ describe('Connection Pool', function () { } }); - const pool = new ConnectionPool(stubServer, { + pool = new ConnectionPool(stubServer, { maxPoolSize: 1, waitQueueTimeoutMS: 200, hostAddress: mockMongod.hostAddress() @@ -157,7 +162,7 @@ describe('Connection Pool', function () { }); it('should respect the minPoolSizeCheckFrequencyMS option', function () { - const pool = new ConnectionPool(stubServer, { + pool = new ConnectionPool(stubServer, { minPoolSize: 2, minPoolSizeCheckFrequencyMS: 42, hostAddress: mockMongod.hostAddress() @@ -193,7 +198,7 @@ describe('Connection Pool', function () { }); it('should default minPoolSizeCheckFrequencyMS to 100ms', function () { - const pool = new ConnectionPool(stubServer, { + pool = new ConnectionPool(stubServer, { minPoolSize: 2, hostAddress: mockMongod.hostAddress() });