Skip to content

Commit ddd1e81

Browse files
W-A-Jamesdurran
andauthored
feat(NODE-5678): add options parsing support for timeoutMS and defaultTimeoutMS (#4068)
Co-authored-by: Durran Jordan <[email protected]>
1 parent 30cac05 commit ddd1e81

19 files changed

+309
-49
lines changed

src/collection.ts

+2
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ export interface CollectionOptions extends BSONSerializeOptions, WriteConcernOpt
106106
readConcern?: ReadConcernLike;
107107
/** The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST). */
108108
readPreference?: ReadPreferenceLike;
109+
/** @internal TODO(NODE-5688): make this public */
110+
timeoutMS?: number;
109111
}
110112

111113
/** @internal */

src/cursor/abstract_cursor.ts

+3
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ export interface AbstractCursorOptions extends BSONSerializeOptions {
109109
*/
110110
awaitData?: boolean;
111111
noCursorTimeout?: boolean;
112+
/** @internal TODO(NODE-5688): make this public */
113+
timeoutMS?: number;
112114
}
113115

114116
/** @internal */
@@ -184,6 +186,7 @@ export abstract class AbstractCursor<
184186
: ReadPreference.primary,
185187
...pluckBSONSerializeOptions(options)
186188
};
189+
this[kOptions].timeoutMS = options.timeoutMS;
187190

188191
const readConcern = ReadConcern.fromOptions(options);
189192
if (readConcern) {

src/db.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ const DB_OPTIONS_ALLOW_LIST = [
6666
'enableUtf8Validation',
6767
'promoteValues',
6868
'compression',
69-
'retryWrites'
69+
'retryWrites',
70+
'timeoutMS'
7071
];
7172

7273
/** @internal */
@@ -94,6 +95,8 @@ export interface DbOptions extends BSONSerializeOptions, WriteConcernOptions {
9495
readConcern?: ReadConcern;
9596
/** Should retry failed writes */
9697
retryWrites?: boolean;
98+
/** @internal TODO(NODE-5688): make this public */
99+
timeoutMS?: number;
97100
}
98101

99102
/**

src/gridfs/download.ts

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export interface GridFSBucketReadStreamOptions {
2828
* to be returned by the stream. `end` is non-inclusive
2929
*/
3030
end?: number;
31+
/** @internal TODO(NODE-5688): make this public */
32+
timeoutMS?: number;
3133
}
3234

3335
/** @public */

src/gridfs/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export interface GridFSBucketOptions extends WriteConcernOptions {
3636
chunkSizeBytes?: number;
3737
/** Read preference to be passed to read operations */
3838
readPreference?: ReadPreference;
39+
/** @internal TODO(NODE-5688): make this public */
40+
timeoutMS?: number;
3941
}
4042

4143
/** @internal */

src/gridfs/upload.ts

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export interface GridFSBucketWriteStreamOptions extends WriteConcernOptions {
3636
* @deprecated Will be removed in the next major version. Add an aliases field to the metadata document instead.
3737
*/
3838
aliases?: string[];
39+
/** @internal TODO(NODE-5688): make this public */
40+
timeoutMS?: number;
3941
}
4042

4143
/**

src/mongo_client.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export type SupportedNodeConnectionOptions = SupportedTLSConnectionOptions &
120120
export interface MongoClientOptions extends BSONSerializeOptions, SupportedNodeConnectionOptions {
121121
/** Specifies the name of the replica set, if the mongod is a member of a replica set. */
122122
replicaSet?: string;
123-
/** @internal This option is in development and currently has no behaviour. */
123+
/** @internal TODO(NODE-5688): This option is in development and currently has no behaviour. */
124124
timeoutMS?: number;
125125
/** Enables or disables TLS/SSL for the connection. */
126126
tls?: boolean;
@@ -897,4 +897,6 @@ export interface MongoOptions
897897
* TODO: NODE-5671 - remove internal flag
898898
*/
899899
mongodbLogPath?: 'stderr' | 'stdout' | MongoDBLogWritable;
900+
/** @internal TODO(NODE-5688): make this public */
901+
timeoutMS?: number;
900902
}

src/operations/execute_operation.ts

+5
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ export async function executeOperation<
110110
} else if (session.client !== client) {
111111
throw new MongoInvalidArgumentError('ClientSession must be from the same MongoClient');
112112
}
113+
if (session.explicit && session?.timeoutMS != null && operation.options.timeoutMS != null) {
114+
throw new MongoInvalidArgumentError(
115+
'Do not specify timeoutMS on operation if already specified on an explicit session'
116+
);
117+
}
113118

114119
const readPreference = operation.readPreference ?? ReadPreference.primary;
115120
const inTransaction = !!session?.inTransaction();

src/operations/operation.ts

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export interface OperationOptions extends BSONSerializeOptions {
3434
/** @internal Hints to `executeOperation` that this operation should not unpin on an ended transaction */
3535
bypassPinningCheck?: boolean;
3636
omitReadPreference?: boolean;
37+
38+
/** @internal TODO(NODE-5688): make this public */
39+
timeoutMS?: number;
3740
}
3841

3942
/** @internal */

src/sessions.ts

+6
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ export interface ClientSessionOptions {
6161
snapshot?: boolean;
6262
/** The default TransactionOptions to use for transactions started on this session. */
6363
defaultTransactionOptions?: TransactionOptions;
64+
/** @internal
65+
* The value of timeoutMS used for CSOT. Used to override client timeoutMS */
66+
defaultTimeoutMS?: number;
6467

6568
/** @internal */
6669
owner?: symbol | AbstractCursor;
@@ -131,6 +134,8 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
131134
[kPinnedConnection]?: Connection;
132135
/** @internal */
133136
[kTxnNumberIncrement]: number;
137+
/** @internal */
138+
timeoutMS?: number;
134139

135140
/**
136141
* Create a client session.
@@ -173,6 +178,7 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
173178
this.sessionPool = sessionPool;
174179
this.hasEnded = false;
175180
this.clientOptions = clientOptions;
181+
this.timeoutMS = options.defaultTimeoutMS ?? client.options?.timeoutMS;
176182

177183
this.explicit = !!options.explicit;
178184
this[kServerSession] = this.explicit ? this.sessionPool.acquire() : null;
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,97 @@
1-
/* eslint-disable @typescript-eslint/no-empty-function */
21
/* Anything javascript specific relating to timeouts */
2+
import { expect } from 'chai';
3+
import * as sinon from 'sinon';
34

4-
describe.skip('CSOT driver tests', () => {});
5+
import {
6+
type ClientSession,
7+
type Collection,
8+
type Db,
9+
type FindCursor,
10+
type MongoClient
11+
} from '../../mongodb';
12+
13+
describe('CSOT driver tests', () => {
14+
afterEach(() => {
15+
sinon.restore();
16+
});
17+
18+
describe('timeoutMS inheritance', () => {
19+
let client: MongoClient;
20+
let db: Db;
21+
let coll: Collection;
22+
23+
beforeEach(async function () {
24+
client = this.configuration.newClient(undefined, { timeoutMS: 100 });
25+
db = client.db('test', { timeoutMS: 200 });
26+
});
27+
28+
afterEach(async () => {
29+
await client?.close();
30+
});
31+
32+
describe('when timeoutMS is provided on an operation', () => {
33+
beforeEach(() => {
34+
coll = db.collection('test', { timeoutMS: 300 });
35+
});
36+
37+
describe('when in a session', () => {
38+
let cursor: FindCursor;
39+
let session: ClientSession;
40+
41+
beforeEach(() => {
42+
session = client.startSession({ defaultTimeoutMS: 400 });
43+
cursor = coll.find({}, { session, timeoutMS: 500 });
44+
});
45+
46+
afterEach(async () => {
47+
await cursor?.close();
48+
await session?.endSession();
49+
await session.endSession();
50+
});
51+
52+
it('throws an error', async () => {
53+
expect(cursor.cursorOptions).to.have.property('timeoutMS', 500);
54+
});
55+
});
56+
57+
describe('when not in a session', () => {
58+
let cursor: FindCursor;
59+
60+
beforeEach(() => {
61+
db = client.db('test', { timeoutMS: 200 });
62+
coll = db.collection('test', { timeoutMS: 300 });
63+
cursor = coll.find({}, { timeoutMS: 400 });
64+
});
65+
66+
afterEach(async () => {
67+
await cursor?.close();
68+
});
69+
70+
it('overrides the value provided on the db', async () => {
71+
expect(cursor.cursorOptions).to.have.property('timeoutMS', 400);
72+
});
73+
});
74+
});
75+
76+
describe('when timeoutMS is provided on a collection', () => {
77+
beforeEach(() => {
78+
db = client.db('test', { timeoutMS: 200 });
79+
coll = db.collection('test', { timeoutMS: 300 });
80+
});
81+
82+
it('overrides the value provided on the db', () => {
83+
expect(coll.s.options).to.have.property('timeoutMS', 300);
84+
});
85+
86+
describe('when timeoutMS is provided on a db', () => {
87+
beforeEach(() => {
88+
db = client.db('test', { timeoutMS: 200 });
89+
});
90+
91+
it('overrides the value provided on the client', () => {
92+
expect(db.s.options).to.have.property('timeoutMS', 200);
93+
});
94+
});
95+
});
96+
});
97+
});

test/spec/uri-options/connection-options.json

+32-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"tests": [
33
{
44
"description": "Valid connection and timeout options are parsed correctly",
5-
"uri": "mongodb://example.com/?appname=URI-OPTIONS-SPEC-TEST&connectTimeoutMS=20000&heartbeatFrequencyMS=5000&localThresholdMS=3000&maxIdleTimeMS=50000&replicaSet=uri-options-spec&retryWrites=true&serverSelectionTimeoutMS=15000&socketTimeoutMS=7500",
5+
"uri": "mongodb://example.com/?appname=URI-OPTIONS-SPEC-TEST&connectTimeoutMS=20000&heartbeatFrequencyMS=5000&localThresholdMS=3000&maxIdleTimeMS=50000&replicaSet=uri-options-spec&retryWrites=true&serverSelectionTimeoutMS=15000&socketTimeoutMS=7500&timeoutMS=100",
66
"valid": true,
77
"warning": false,
88
"hosts": null,
@@ -16,7 +16,8 @@
1616
"replicaSet": "uri-options-spec",
1717
"retryWrites": true,
1818
"serverSelectionTimeoutMS": 15000,
19-
"socketTimeoutMS": 7500
19+
"socketTimeoutMS": 7500,
20+
"timeoutMS": 100
2021
}
2122
},
2223
{
@@ -238,6 +239,35 @@
238239
"hosts": null,
239240
"auth": null,
240241
"options": {}
242+
},
243+
{
244+
"description": "timeoutMS=0",
245+
"uri": "mongodb://example.com/?timeoutMS=0",
246+
"valid": true,
247+
"warning": false,
248+
"hosts": null,
249+
"auth": null,
250+
"options": {
251+
"timeoutMS": 0
252+
}
253+
},
254+
{
255+
"description": "Non-numeric timeoutMS causes a warning",
256+
"uri": "mongodb://example.com/?timeoutMS=invalid",
257+
"valid": true,
258+
"warning": true,
259+
"hosts": null,
260+
"auth": null,
261+
"options": {}
262+
},
263+
{
264+
"description": "Too low timeoutMS causes a warning",
265+
"uri": "mongodb://example.com/?timeoutMS=-2",
266+
"valid": true,
267+
"warning": true,
268+
"hosts": null,
269+
"auth": null,
270+
"options": {}
241271
}
242272
]
243273
}

test/spec/uri-options/connection-options.yml

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
tests:
22
-
33
description: "Valid connection and timeout options are parsed correctly"
4-
uri: "mongodb://example.com/?appname=URI-OPTIONS-SPEC-TEST&connectTimeoutMS=20000&heartbeatFrequencyMS=5000&localThresholdMS=3000&maxIdleTimeMS=50000&replicaSet=uri-options-spec&retryWrites=true&serverSelectionTimeoutMS=15000&socketTimeoutMS=7500"
4+
uri: "mongodb://example.com/?appname=URI-OPTIONS-SPEC-TEST&connectTimeoutMS=20000&heartbeatFrequencyMS=5000&localThresholdMS=3000&maxIdleTimeMS=50000&replicaSet=uri-options-spec&retryWrites=true&serverSelectionTimeoutMS=15000&socketTimeoutMS=7500&timeoutMS=100"
55
valid: true
66
warning: false
77
hosts: ~
@@ -16,6 +16,7 @@ tests:
1616
retryWrites: true
1717
serverSelectionTimeoutMS: 15000
1818
socketTimeoutMS: 7500
19+
timeoutMS: 100
1920
-
2021
description: "Non-numeric connectTimeoutMS causes a warning"
2122
uri: "mongodb://example.com/?connectTimeoutMS=invalid"
@@ -64,7 +65,7 @@ tests:
6465
hosts: ~
6566
auth: ~
6667
options: {}
67-
-
68+
-
6869
description: "Invalid retryWrites causes a warning"
6970
uri: "mongodb://example.com/?retryWrites=invalid"
7071
valid: true
@@ -207,3 +208,28 @@ tests:
207208
hosts: ~
208209
auth: ~
209210
options: {}
211+
-
212+
description: "timeoutMS=0"
213+
uri: "mongodb://example.com/?timeoutMS=0"
214+
valid: true
215+
warning: false
216+
hosts: ~
217+
auth: ~
218+
options:
219+
timeoutMS: 0
220+
-
221+
description: "Non-numeric timeoutMS causes a warning"
222+
uri: "mongodb://example.com/?timeoutMS=invalid"
223+
valid: true
224+
warning: true
225+
hosts: ~
226+
auth: ~
227+
options: {}
228+
-
229+
description: "Too low timeoutMS causes a warning"
230+
uri: "mongodb://example.com/?timeoutMS=-2"
231+
valid: true
232+
warning: true
233+
hosts: ~
234+
auth: ~
235+
options: {}

test/spec/uri-options/connection-pool-options.yml

-1
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,3 @@ tests:
6565
hosts: ~
6666
auth: ~
6767
options: {}
68-

test/spec/uri-options/read-preference-options.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
},
2424
{
2525
"description": "Single readPreferenceTags is parsed as array of size one",
26-
"uri": "mongodb://example.com/?readPreferenceTags=dc:ny",
26+
"uri": "mongodb://example.com/?readPreference=secondary&readPreferenceTags=dc:ny",
2727
"valid": true,
2828
"warning": false,
2929
"hosts": null,

test/spec/uri-options/read-preference-options.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ tests:
1717
maxStalenessSeconds: 120
1818
-
1919
description: "Single readPreferenceTags is parsed as array of size one"
20-
uri: "mongodb://example.com/?readPreferenceTags=dc:ny"
20+
uri: "mongodb://example.com/?readPreference=secondary&readPreferenceTags=dc:ny"
2121
valid: true
2222
warning: false
2323
hosts: ~

test/tools/uri_spec_runner.ts

+1
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ export function executeUriValidationTest(
349349
case 'maxConnecting':
350350
case 'maxPoolSize':
351351
case 'minPoolSize':
352+
case 'timeoutMS':
352353
case 'connectTimeoutMS':
353354
case 'heartbeatFrequencyMS':
354355
case 'localThresholdMS':

0 commit comments

Comments
 (0)