Skip to content

Commit 7cbb641

Browse files
fix(NODE-6589): background task does not prune idle connections when minPoolSize=0 (#4569)
Co-authored-by: Durran Jordan <[email protected]>
1 parent 54c5740 commit 7cbb641

File tree

6 files changed

+74
-13
lines changed

6 files changed

+74
-13
lines changed

src/cmap/connection_pool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ export class ConnectionPool extends TypedEventEmitter<ConnectionPoolEvents> {
691691

692692
private ensureMinPoolSize() {
693693
const minPoolSize = this.options.minPoolSize;
694-
if (this.poolState !== PoolState.ready || minPoolSize === 0) {
694+
if (this.poolState !== PoolState.ready) {
695695
return;
696696
}
697697

test/integration/connection-monitoring-and-pooling/connection_pool.test.ts

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import { once } from 'node:events';
22

33
import { expect } from 'chai';
4-
5-
import { type ConnectionPoolCreatedEvent, type Db, type MongoClient } from '../../mongodb';
4+
import * as sinon from 'sinon';
5+
6+
import {
7+
type ConnectionPoolCreatedEvent,
8+
type Db,
9+
type MongoClient,
10+
type Server
11+
} from '../../mongodb';
612
import { clearFailPoint, configureFailPoint, sleep } from '../../tools/utils';
713

814
describe('Connection Pool', function () {
@@ -150,4 +156,51 @@ describe('Connection Pool', function () {
150156
});
151157
});
152158
});
159+
160+
describe(
161+
'background task cleans up connections when minPoolSize=0',
162+
{ requires: { topology: 'single' } },
163+
function () {
164+
let server: Server;
165+
let ensureMinPoolSizeSpy: sinon.SinonSpy;
166+
167+
beforeEach(async function () {
168+
client = this.configuration.newClient(
169+
{},
170+
{
171+
maxConnecting: 10,
172+
minPoolSize: 0,
173+
maxIdleTimeMS: 100
174+
}
175+
);
176+
177+
await client.connect();
178+
179+
await Promise.all(
180+
Array.from({ length: 10 }).map(() => {
181+
return client.db('foo').collection('bar').insertOne({ a: 1 });
182+
})
183+
);
184+
185+
server = Array.from(client.topology.s.servers.entries())[0][1];
186+
expect(
187+
server.pool.availableConnectionCount,
188+
'pool was not filled with connections'
189+
).to.be.greaterThan(0);
190+
191+
ensureMinPoolSizeSpy = sinon.spy(server.pool, 'ensureMinPoolSize');
192+
});
193+
194+
it(
195+
'prunes idle connections when minPoolSize=0',
196+
{ requires: { topology: 'single' } },
197+
async function () {
198+
await sleep(500);
199+
expect(server.pool.availableConnectionCount).to.equal(0);
200+
201+
expect(ensureMinPoolSizeSpy).to.have.been.called;
202+
}
203+
);
204+
}
205+
);
153206
});

test/unit/assorted/max_staleness.spec.test.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,12 @@ describe('Max Staleness (spec)', function () {
4545
});
4646

4747
const specTests = collectStalenessTests(maxStalenessDir);
48-
Object.keys(specTests).forEach(specTestName => {
48+
for (const [specTestName, test] of Object.entries(specTests)) {
4949
describe(specTestName, () => {
50-
specTests[specTestName].forEach(testData => {
50+
for (const testData of test)
5151
it(testData.description, async function () {
5252
return executeServerSelectionTest(testData);
5353
});
54-
});
5554
});
56-
});
55+
}
5756
});

test/unit/assorted/server_selection_latency_window_utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,6 @@ export async function runServerSelectionLatencyWindowTest(test: ServerSelectionL
145145
const observedFrequencies = calculateObservedFrequencies(selectedServers);
146146

147147
compareResultsToExpected(test.outcome, observedFrequencies);
148+
149+
await topology.close();
148150
}

test/unit/assorted/server_selection_spec_helper.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ async function executeServerSelectionTest(testDefinition) {
123123
const readPreference = readPreferenceFromDefinition(testDefinition.read_preference);
124124
selector = ServerSelectors.readPreferenceServerSelector(readPreference);
125125
} catch (e) {
126-
if (testDefinition.error) return;
126+
if (testDefinition.error) return topology.close();
127127
throw e;
128128
}
129129
} else {
@@ -179,6 +179,8 @@ async function executeServerSelectionTest(testDefinition) {
179179
// this is another expected error case
180180
if (expectedServers.length === 0 && err instanceof MongoServerSelectionError) return;
181181
throw err;
182+
} finally {
183+
topology.close();
182184
}
183185
}
184186

test/unit/cmap/connection_pool.test.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const { TimeoutContext } = require('../../mongodb');
1515
describe('Connection Pool', function () {
1616
let timeoutContext;
1717
let mockMongod;
18+
let pool;
1819
const stubServer = {
1920
topology: {
2021
client: {
@@ -50,6 +51,8 @@ describe('Connection Pool', function () {
5051
timeoutContext = TimeoutContext.create({ waitQueueTimeoutMS: 0, serverSelectionTimeoutMS: 0 });
5152
});
5253

54+
afterEach(() => pool?.close());
55+
5356
it('should destroy connections which have been closed', async function () {
5457
mockMongod.setMessageHandler(request => {
5558
const doc = request.document;
@@ -61,7 +64,7 @@ describe('Connection Pool', function () {
6164
}
6265
});
6366

64-
const pool = new ConnectionPool(stubServer, {
67+
pool = new ConnectionPool(stubServer, {
6568
maxPoolSize: 1,
6669
hostAddress: mockMongod.hostAddress()
6770
});
@@ -81,6 +84,8 @@ describe('Connection Pool', function () {
8184
expect(events).to.have.length(1);
8285
const closeEvent = events[0];
8386
expect(closeEvent).have.property('reason').equal('error');
87+
88+
conn.destroy();
8489
});
8590

8691
it('should propagate socket timeouts to connections', async function () {
@@ -93,7 +98,7 @@ describe('Connection Pool', function () {
9398
}
9499
});
95100

96-
const pool = new ConnectionPool(stubServer, {
101+
pool = new ConnectionPool(stubServer, {
97102
maxPoolSize: 1,
98103
socketTimeoutMS: 200,
99104
hostAddress: mockMongod.hostAddress()
@@ -117,7 +122,7 @@ describe('Connection Pool', function () {
117122
}
118123
});
119124

120-
const pool = new ConnectionPool(stubServer, {
125+
pool = new ConnectionPool(stubServer, {
121126
maxPoolSize: 1,
122127
waitQueueTimeoutMS: 200,
123128
hostAddress: mockMongod.hostAddress()
@@ -157,7 +162,7 @@ describe('Connection Pool', function () {
157162
});
158163

159164
it('should respect the minPoolSizeCheckFrequencyMS option', function () {
160-
const pool = new ConnectionPool(stubServer, {
165+
pool = new ConnectionPool(stubServer, {
161166
minPoolSize: 2,
162167
minPoolSizeCheckFrequencyMS: 42,
163168
hostAddress: mockMongod.hostAddress()
@@ -193,7 +198,7 @@ describe('Connection Pool', function () {
193198
});
194199

195200
it('should default minPoolSizeCheckFrequencyMS to 100ms', function () {
196-
const pool = new ConnectionPool(stubServer, {
201+
pool = new ConnectionPool(stubServer, {
197202
minPoolSize: 2,
198203
hostAddress: mockMongod.hostAddress()
199204
});

0 commit comments

Comments
 (0)