Skip to content

Commit 7cd3efe

Browse files
committed
wip
1 parent 450b163 commit 7cd3efe

File tree

3 files changed

+76
-6
lines changed

3 files changed

+76
-6
lines changed

src/mongo_client.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { readPreferenceServerSelector } from './sdam/server_selection';
4747
import type { SrvPoller } from './sdam/srv_polling';
4848
import { Topology, type TopologyEvents } from './sdam/topology';
4949
import { ClientSession, type ClientSessionOptions, ServerSessionPool } from './sessions';
50+
import { Timeout } from './timeout';
5051
import {
5152
COSMOS_DB_CHECK,
5253
COSMOS_DB_MSG,
@@ -516,13 +517,19 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> implements
516517
return await this.connectionLock;
517518
}
518519

519-
try {
520-
this.connectionLock = this._connect();
521-
await this.connectionLock;
522-
} finally {
523-
// release
520+
const timeoutMS = this[kOptions].timeoutMS;
521+
522+
this.connectionLock = this._connect().finally(() => {
523+
// Clear the lock only when this promise finishes:
524524
this.connectionLock = undefined;
525-
}
525+
});
526+
527+
await (timeoutMS == null
528+
? this.connectionLock
529+
: Promise.race([
530+
this.connectionLock,
531+
Timeout.expires(timeoutMS).catch(Timeout.convertError)
532+
]));
526533

527534
return this;
528535
}

src/timeout.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ export class Timeout extends Promise<never> {
126126
static override reject(rejection?: Error): Timeout {
127127
return new Timeout(undefined, { duration: 0, unref: true, rejection });
128128
}
129+
130+
static convertError(this: void, cause: unknown): never {
131+
if (TimeoutError.is(cause)) throw new MongoOperationTimeoutError('Timed out');
132+
if (cause instanceof Error) throw cause;
133+
throw new MongoRuntimeError('Unknown error', { cause: cause as any });
134+
}
129135
}
130136

131137
/** @internal */

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import {
88
type Collection,
99
type MongoClient,
1010
MongoNotConnectedError,
11+
MongoOperationTimeoutError,
1112
ProfilingLevel,
1213
Topology,
1314
TopologyType
1415
} from '../../mongodb';
16+
import { type FailPoint } from '../../tools/utils';
1517

1618
describe('When executing an operation for the first time', () => {
1719
let client: MongoClient;
@@ -821,4 +823,59 @@ describe('When executing an operation for the first time', () => {
821823
});
822824
});
823825
});
826+
827+
describe.only('and CSOT is enabled', function () {
828+
let client: MongoClient;
829+
830+
beforeEach(async function () {
831+
client = this.configuration.newClient({ timeoutMS: 1000 });
832+
});
833+
834+
afterEach(async function () {
835+
await client.close();
836+
});
837+
838+
describe('and nothing is wrong', function () {
839+
it('should connect the client', async function () {
840+
await client.connect();
841+
expect(client).to.have.property('topology').that.is.instanceOf(Topology);
842+
});
843+
});
844+
845+
describe('and the server requires auth and ping is delayed', function () {
846+
beforeEach(async function () {
847+
// set failpoint to delay ping
848+
// create new util client to avoid affecting the test client
849+
const utilClient = this.configuration.newClient();
850+
await utilClient.db('admin').command({
851+
configureFailPoint: 'failCommand',
852+
mode: { times: 1 },
853+
data: { failCommands: ['ping'], blockConnection: true, blockTimeMS: 2000 }
854+
} as FailPoint);
855+
await utilClient.close();
856+
});
857+
858+
it(
859+
'should throw an MongoOperationTimeoutError error from connect',
860+
{ requires: { auth: 'enabled' } },
861+
async function () {
862+
const start = performance.now();
863+
const error = await client.connect().catch(error => error);
864+
const end = performance.now();
865+
expect(error).to.be.instanceOf(MongoOperationTimeoutError);
866+
expect(end - start).to.be.within(1000, 1500);
867+
}
868+
);
869+
870+
it(
871+
'still have a pending connect promise',
872+
{ requires: { auth: 'enabled' } },
873+
async function () {
874+
const error = await client.connect().catch(error => error);
875+
expect(client).to.have.property('connectionLock').that.is.instanceOf(Promise);
876+
expect(error).to.be.instanceOf(MongoOperationTimeoutError);
877+
}
878+
);
879+
});
880+
});
824881
});

0 commit comments

Comments
 (0)