Skip to content

Commit c4b4600

Browse files
nbbeekenaditi-khare-mongoDBdariakp
committed
docs(NODE-6223): timeoutMS does not govern auto-connect (#4280)
Co-authored-by: Aditi Khare <[email protected]> Co-authored-by: Daria Pardue <[email protected]>
1 parent 583024c commit c4b4600

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

src/mongo_client.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,13 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
513513
/**
514514
* Connect to MongoDB using a url
515515
*
516+
* @remarks
517+
* Calling `connect` is optional since the first operation you perform will call `connect` if it's needed.
518+
* `timeoutMS` will bound the time any operation can take before throwing a timeout error.
519+
* However, when the operation being run is automatically connecting your `MongoClient` the `timeoutMS` will not apply to the time taken to connect the MongoClient.
520+
* This means the time to setup the `MongoClient` does not count against `timeoutMS`.
521+
* If you are using `timeoutMS` we recommend connecting your client explicitly in advance of any operation to avoid this inconsistent execution time.
522+
*
516523
* @see docs.mongodb.org/manual/reference/connection-string/
517524
*/
518525
async connect(): Promise<this> {
@@ -710,6 +717,13 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
710717
* Connect to MongoDB using a url
711718
*
712719
* @remarks
720+
* Calling `connect` is optional since the first operation you perform will call `connect` if it's needed.
721+
* `timeoutMS` will bound the time any operation can take before throwing a timeout error.
722+
* However, when the operation being run is automatically connecting your `MongoClient` the `timeoutMS` will not apply to the time taken to connect the MongoClient.
723+
* This means the time to setup the `MongoClient` does not count against `timeoutMS`.
724+
* If you are using `timeoutMS` we recommend connecting your client explicitly in advance of any operation to avoid this inconsistent execution time.
725+
*
726+
* @remarks
713727
* The programmatically provided options take precedence over the URI options.
714728
*
715729
* @see https://www.mongodb.com/docs/manual/reference/connection-string/

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

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import { expect } from 'chai';
22
import { once } from 'events';
3+
import * as sinon from 'sinon';
34

45
import {
56
BSONType,
67
type ChangeStream,
78
ClientSession,
89
type Collection,
9-
type MongoClient,
10+
MongoClient,
1011
MongoNotConnectedError,
1112
ProfilingLevel,
1213
Topology,
1314
TopologyType
1415
} from '../../mongodb';
16+
import { type FailPoint, sleep } from '../../tools/utils';
1517

1618
describe('When executing an operation for the first time', () => {
1719
let client: MongoClient;
@@ -821,4 +823,104 @@ describe('When executing an operation for the first time', () => {
821823
});
822824
});
823825
});
826+
827+
describe('when CSOT is enabled', function () {
828+
let client: MongoClient;
829+
830+
beforeEach(async function () {
831+
client = this.configuration.newClient({ timeoutMS: 500 });
832+
});
833+
834+
afterEach(async function () {
835+
await client.close();
836+
});
837+
838+
describe('when nothing is wrong', function () {
839+
it('connects the client', async function () {
840+
await client.connect();
841+
expect(client).to.have.property('topology').that.is.instanceOf(Topology);
842+
});
843+
});
844+
845+
describe(
846+
'when the server requires auth and ping is delayed',
847+
{ requires: { auth: 'enabled', mongodb: '>=4.4' } },
848+
function () {
849+
beforeEach(async function () {
850+
// set failpoint to delay ping
851+
// create new util client to avoid affecting the test client
852+
const utilClient = this.configuration.newClient();
853+
await utilClient.db('admin').command({
854+
configureFailPoint: 'failCommand',
855+
mode: { times: 1 },
856+
data: { failCommands: ['ping'], blockConnection: true, blockTimeMS: 1000 }
857+
} as FailPoint);
858+
await utilClient.close();
859+
});
860+
861+
it('timeoutMS from the client is not used for the internal `ping`', async function () {
862+
const start = performance.now();
863+
const returnedClient = await client.connect();
864+
const end = performance.now();
865+
expect(returnedClient).to.equal(client);
866+
expect(end - start).to.be.within(1000, 1500); // timeoutMS is 1000, did not apply.
867+
});
868+
}
869+
);
870+
871+
describe(
872+
'when server selection takes longer than the timeout',
873+
{ requires: { auth: 'enabled', mongodb: '>=4.4' } },
874+
function () {
875+
beforeEach(async function () {
876+
const selectServerStub = sinon
877+
.stub(Topology.prototype, 'selectServer')
878+
.callsFake(async function (selector, options) {
879+
await sleep(1000);
880+
const result = selectServerStub.wrappedMethod.call(this, selector, options);
881+
sinon.restore(); // restore after connect selection
882+
return result;
883+
});
884+
});
885+
886+
// restore sinon stub after test
887+
afterEach(() => {
888+
sinon.restore();
889+
});
890+
891+
it('client.connect() takes as long as selectServer is delayed for and does not throw a timeout error', async function () {
892+
const start = performance.now();
893+
expect(client.topology).to.not.exist; // make sure not connected.
894+
const res = await client.db().collection('test').insertOne({ a: 1 }, { timeoutMS: 500 }); // auto-connect
895+
const end = performance.now();
896+
expect(res).to.have.property('acknowledged', true);
897+
expect(end - start).to.be.within(1000, 1500); // timeoutMS is 1000, did not apply.
898+
});
899+
}
900+
);
901+
902+
describe('when auto connect is used and connect() takes longer than timeoutMS', function () {
903+
// This test stubs the connect method to check that connect() does not get timed out
904+
// vs. the test above makes sure that the `ping` does not inherit the client's timeoutMS setting
905+
beforeEach(async function () {
906+
const connectStub = sinon
907+
.stub(MongoClient.prototype, 'connect')
908+
.callsFake(async function () {
909+
await sleep(1000);
910+
const result = connectStub.wrappedMethod.call(this);
911+
sinon.restore(); // restore after connect selection
912+
return result;
913+
});
914+
});
915+
916+
it('the operation succeeds', async function () {
917+
const start = performance.now();
918+
expect(client.topology).to.not.exist; // make sure not connected.
919+
const res = await client.db().collection('test').insertOne({ a: 1 }); // auto-connect
920+
const end = performance.now();
921+
expect(res).to.have.property('acknowledged', true);
922+
expect(end - start).to.be.within(1000, 1500); // timeoutMS is 1000, did not apply.
923+
});
924+
});
925+
});
824926
});

0 commit comments

Comments
 (0)