Skip to content

Commit

Permalink
srv test
Browse files Browse the repository at this point in the history
lint
  • Loading branch information
aditi-khare-mongoDB committed Jan 10, 2025
1 parent 3338fb9 commit 4865259
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 120 deletions.
282 changes: 168 additions & 114 deletions test/integration/node-specific/client_close.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { type TestConfiguration } from '../../tools/runner/config';
import { runScriptAndGetProcessInfo } from './resource_tracking_script_builder';

describe.only('MongoClient.close() Integration', () => {
describe.skip('MongoClient.close() Integration', () => {
// note: these tests are set-up in accordance of the resource ownership tree

let config: TestConfiguration;
Expand Down Expand Up @@ -76,33 +76,45 @@ describe.only('MongoClient.close() Integration', () => {
describe('Node.js resource: Server Selection Timer', () => {
describe('after a Topology is created through client.connect()', () => {
it('server selection timers are cleaned up by client.close()', async () => {
const run = async function ({ MongoClient, uri, expect, sinon, sleep, mongodb, getTimerCount }) {
const serverSelectionTimeoutMS = 2222;
const client = new MongoClient(uri, { minPoolSize: 1, serverSelectionTimeoutMS });
const timers = require('timers');
const timeoutStartedSpy = sinon.spy(timers, 'setTimeout');
let serverSelectionTimeoutStarted = false;

// make server selection hang so check out timer isn't cleared and check that the timeout has started
sinon.stub(Promise, 'race').callsFake(async ([serverPromise, timeout]) => {
serverSelectionTimeoutStarted = timeoutStartedSpy.getCalls().filter(r => r.args.includes(serverSelectionTimeoutMS)).flat().length > 0;
await timeout;
});
const run = async function ({
MongoClient,
uri,
expect,
sinon,
sleep,
mongodb,
getTimerCount,
timers
}) {
const serverSelectionTimeoutMS = 2222;
const client = new MongoClient(uri, { minPoolSize: 1, serverSelectionTimeoutMS });
const timeoutStartedSpy = sinon.spy(timers, 'setTimeout');
let serverSelectionTimeoutStarted = false;

// make server selection hang so check out timer isn't cleared and check that the timeout has started
sinon.stub(Promise, 'race').callsFake(async ([_serverPromise, timeout]) => {
serverSelectionTimeoutStarted =
timeoutStartedSpy
.getCalls()
.filter(r => r.args.includes(serverSelectionTimeoutMS))
.flat().length > 0;
await timeout;
});

const insertPromise = client.db('db').collection('collection').insertOne({ x: 1 });
const insertPromise = client.db('db').collection('collection').insertOne({ x: 1 });

// don't allow entire server selection timer to elapse to ensure close is called mid-timeout
await sleep(serverSelectionTimeoutMS / 2);
expect(serverSelectionTimeoutStarted).to.be.true;
// don't allow entire server selection timer to elapse to ensure close is called mid-timeout
await sleep(serverSelectionTimeoutMS / 2);
expect(serverSelectionTimeoutStarted).to.be.true;

expect(getTimerCount()).to.not.equal(0);
await client.close();
expect(getTimerCount()).to.equal(0);
expect(getTimerCount()).to.not.equal(0);
await client.close();
expect(getTimerCount()).to.equal(0);

const err = await insertPromise.catch(e => e);
expect(err).to.be.instanceOf(mongodb.MongoServerSelectionError);
};
await runScriptAndGetProcessInfo('timer-server-selection', config, run);
const err = await insertPromise.catch(e => e);
expect(err).to.be.instanceOf(mongodb.MongoServerSelectionError);
};
await runScriptAndGetProcessInfo('timer-server-selection', config, run);
});
});
});
Expand All @@ -119,57 +131,66 @@ describe.only('MongoClient.close() Integration', () => {
describe('MonitorInterval', () => {
describe('Node.js resource: Timer', () => {
describe('after a new monitor is made', () => {
it('monitor interval timer is cleaned up by client.close()', metadata, async function () {
const run = async function ({ MongoClient, uri, expect, sleep, getTimerCount }) {
const heartbeatFrequencyMS = 2000;
const client = new MongoClient(uri, { heartbeatFrequencyMS });
let heartbeatHappened = false;
client.on('serverHeartbeatSucceeded', () => heartbeatHappened = true);
await client.connect();
await sleep(heartbeatFrequencyMS * 2.5);
expect(heartbeatHappened).to.be.true;
it(
'monitor interval timer is cleaned up by client.close()',
metadata,
async function () {
const run = async function ({ MongoClient, uri, expect, sleep, getTimerCount }) {
const heartbeatFrequencyMS = 2000;
const client = new MongoClient(uri, { heartbeatFrequencyMS });
let heartbeatHappened = false;
client.on('serverHeartbeatSucceeded', () => (heartbeatHappened = true));
await client.connect();
await sleep(heartbeatFrequencyMS * 2.5);
expect(heartbeatHappened).to.be.true;

function getMonitorTimer(servers) {
for (const server of servers) {
return server[1]?.monitor.monitorId.timerId;
function getMonitorTimer(servers) {
for (const server of servers) {
return server[1]?.monitor.monitorId.timerId;
}
}
};
const servers = client.topology.s.servers;
expect(getMonitorTimer(servers)).to.exist;
await client.close();
expect(getMonitorTimer(servers)).to.not.exist;
const servers = client.topology.s.servers;
expect(getMonitorTimer(servers)).to.exist;
await client.close();
expect(getMonitorTimer(servers)).to.not.exist;

expect(getTimerCount()).to.equal(0);
};
await runScriptAndGetProcessInfo('timer-monitor-interval', config, run);
});
expect(getTimerCount()).to.equal(0);
};
await runScriptAndGetProcessInfo('timer-monitor-interval', config, run);
}
);
});

describe('after a heartbeat fails', () => {
it('the new monitor interval timer is cleaned up by client.close()', metadata, async () => {
const run = async function ({ MongoClient, uri, expect, sleep, getTimerCount }) {
const heartbeatFrequencyMS = 2000;
const client = new MongoClient('mongodb://fakeUri', { heartbeatFrequencyMS });
let heartbeatHappened = false;
client.on('serverHeartbeatFailed', () => heartbeatHappened = true);
client.connect();
await sleep(heartbeatFrequencyMS * 2.5);
expect(heartbeatHappened).to.be.true;

function getMonitorTimer(servers) {
for (const server of servers) {
return server[1]?.monitor.monitorId.timerId;
it(
'the new monitor interval timer is cleaned up by client.close()',
metadata,
async () => {
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
const run = async function ({ MongoClient, uri, expect, sleep, getTimerCount }) {
const heartbeatFrequencyMS = 2000;
const client = new MongoClient('mongodb://fakeUri', { heartbeatFrequencyMS });
let heartbeatHappened = false;
client.on('serverHeartbeatFailed', () => (heartbeatHappened = true));
client.connect();
await sleep(heartbeatFrequencyMS * 2.5);
expect(heartbeatHappened).to.be.true;

function getMonitorTimer(servers) {
for (const server of servers) {
return server[1]?.monitor.monitorId.timerId;
}
}
};
const servers = client.topology.s.servers;
expect(getMonitorTimer(servers)).to.exist;
await client.close();
expect(getMonitorTimer(servers)).to.not.exist;
const servers = client.topology.s.servers;
expect(getMonitorTimer(servers)).to.exist;
await client.close();
expect(getMonitorTimer(servers)).to.not.exist;

expect(getTimerCount()).to.equal(0);
};
await runScriptAndGetProcessInfo('timer-heartbeat-failed-monitor', config, run);
});
expect(getTimerCount()).to.equal(0);
};
await runScriptAndGetProcessInfo('timer-heartbeat-failed-monitor', config, run);
}
);
});
});
});
Expand Down Expand Up @@ -211,36 +232,40 @@ describe.only('MongoClient.close() Integration', () => {
describe('RTT Pinger', () => {
describe('Node.js resource: Timer', () => {
describe('after entering monitor streaming mode ', () => {
it('the rtt pinger timer is cleaned up by client.close()', metadata, async function () {
const run = async function ({ MongoClient, uri, expect, sleep, getTimerCount }) {
const heartbeatFrequencyMS = 2000;
const client = new MongoClient(uri, {
serverMonitoringMode: 'stream',
heartbeatFrequencyMS
});
await client.connect();
it(
'the rtt pinger timer is cleaned up by client.close()',
metadata,
async function () {
const run = async function ({ MongoClient, uri, expect, sleep, getTimerCount }) {
const heartbeatFrequencyMS = 2000;
const client = new MongoClient(uri, {
serverMonitoringMode: 'stream',
heartbeatFrequencyMS
});
await client.connect();

let heartbeatHappened = false;
client.on('serverHeartbeatSucceeded', () => heartbeatHappened = true);
await sleep(heartbeatFrequencyMS * 2.5);
expect(heartbeatHappened).to.be.true;
let heartbeatHappened = false;
client.on('serverHeartbeatSucceeded', () => (heartbeatHappened = true));
await sleep(heartbeatFrequencyMS * 2.5);
expect(heartbeatHappened).to.be.true;

function getRttTimer(servers) {
for (const server of servers) {
return server[1]?.monitor.rttPinger.monitorId;
function getRttTimer(servers) {
for (const server of servers) {
return server[1]?.monitor.rttPinger.monitorId;
}
}
};

const servers = client.topology.s.servers;
expect(getRttTimer(servers)).to.exist;
const servers = client.topology.s.servers;
expect(getRttTimer(servers)).to.exist;

await client.close();
expect(getRttTimer(servers)).to.not.exist;
await client.close();
expect(getRttTimer(servers)).to.not.exist;

expect(getTimerCount()).to.equal(0);
};
await runScriptAndGetProcessInfo('timer-rtt-monitor', config, run);
});
expect(getTimerCount()).to.equal(0);
};
await runScriptAndGetProcessInfo('timer-rtt-monitor', config, run);
}
);
});
});

Expand Down Expand Up @@ -315,7 +340,6 @@ describe.only('MongoClient.close() Integration', () => {

const servers = client.topology?.s.servers;


function getMinPoolSizeTimer(servers) {
for (const server of servers) {
return server[1].pool.minPoolSizeTimer;
Expand All @@ -336,7 +360,15 @@ describe.only('MongoClient.close() Integration', () => {
describe('Node.js resource: checkOut Timer', () => {
describe('after new connection pool is created', () => {
it('the wait queue timer is cleaned up by client.close()', async function () {
const run = async function ({ MongoClient, uri, expect, sinon, sleep, getTimerCount }) {
const run = async function ({
MongoClient,
uri,
expect,
sinon,
sleep,
getTimerCount,
timers
}) {
const waitQueueTimeoutMS = 1515;

// configure failPoint
Expand All @@ -345,38 +377,56 @@ describe.only('MongoClient.close() Integration', () => {
const failPoint = {
configureFailPoint: 'failCommand',
mode: { times: 1 },
data: { blockConnection: true, blockTimeMS: waitQueueTimeoutMS * 3, failCommands: ['insert'] }
}
data: {
blockConnection: true,
blockTimeMS: waitQueueTimeoutMS * 3,
failCommands: ['insert']
}
};
await utilClient.db('admin').command(failPoint);

const timers = require('timers');
const timeoutStartedSpy = sinon.spy(timers, 'setTimeout');

const client = new MongoClient(uri, { minPoolSize: 1, maxPoolSize: 1, waitQueueTimeoutMS });
const insertPromise = client.db('db').collection('collection').insertOne({ x: 1 }).catch(e => e);
client.db('db').collection('collection').insertOne({ x: 1 }).catch(e => e);
const client = new MongoClient(uri, {
minPoolSize: 1,
maxPoolSize: 1,
waitQueueTimeoutMS
});
const insertPromise = client
.db('db')
.collection('collection')
.insertOne({ x: 1 })
.catch(e => e);
client
.db('db')
.collection('collection')
.insertOne({ x: 1 })
.catch(e => e);

// don't allow entire checkout timer to elapse to ensure close is called mid-timeout
await sleep(waitQueueTimeoutMS / 2);
const checkoutTimeoutStarted = timeoutStartedSpy.getCalls().filter(r => r.args.includes(waitQueueTimeoutMS)).flat().length > 0;
const checkoutTimeoutStarted =
timeoutStartedSpy
.getCalls()
.filter(r => r.args.includes(waitQueueTimeoutMS))
.flat().length > 0;
expect(checkoutTimeoutStarted).to.be.true;

await client.close();
expect(getTimerCount()).to.equal(0);
// un-configure fail{oint
await utilClient
.db()
.admin()
.command({
configureFailPoint: 'failCommand',
mode: 'off'
});
await utilClient.db().admin().command({
configureFailPoint: 'failCommand',
mode: 'off'
});

await utilClient.close();

const err = await insertPromise;
expect(err).to.be.instanceOf(Error);
expect(err.message).to.contain('Timed out while checking out a connection from connection pool');
expect(err.message).to.contain(
'Timed out while checking out a connection from connection pool'
);
};
await runScriptAndGetProcessInfo('timer-check-out', config, run);
});
Expand Down Expand Up @@ -422,19 +472,23 @@ describe.only('MongoClient.close() Integration', () => {
// requires an srv environment that can transition to sharded
const metadata: MongoDBMetadataUI = {
requires: {
predicate: () => process.env.ATLAS_SRV_REPL ? 'Skipped: this test requires an SRV environment' : true
predicate: () =>
process.env.ATLAS_SRV_REPL ? 'Skipped: this test requires an SRV environment' : true
}
};

describe('after SRVPoller is created', () => {
it.only('timers are cleaned up by client.close()', metadata, async () => {
const run = async function ({ MongoClient, uri, expect, sinon, getTimerCount }) {
it('timers are cleaned up by client.close()', metadata, async () => {
const run = async function ({ MongoClient, uri, expect, getTimerCount }) {
const client = new MongoClient(uri);
await client.connect();
const description = client.topology.s.description;
// simulate transition to sharded
client.topology.emit('topologyDescriptionChanged', description, { ... description, type: 'Sharded'});
expect(client.topology.s.srvPoller?._timeout).to.exist;
client.topology.emit('topologyDescriptionChanged', description, {
...description,
type: 'Sharded'
});
expect(client.topology.s.srvPoller._timeout).to.exist;
await client.close();
expect(getTimerCount()).to.equal(0);
};
Expand Down Expand Up @@ -581,4 +635,4 @@ describe.only('MongoClient.close() Integration', () => {
it.skip('all active server-side cursors are closed by client.close()', async function () {});
});
});
});
});
Loading

0 comments on commit 4865259

Please sign in to comment.