mongodb 6.10.0 → 6.11.0-dev.20241123.sha.32f7ac63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/lib/admin.js +3 -2
- package/lib/admin.js.map +1 -1
- package/lib/beta.d.ts +562 -45
- package/lib/bulk/common.js +4 -4
- package/lib/bulk/common.js.map +1 -1
- package/lib/change_stream.js +111 -51
- package/lib/change_stream.js.map +1 -1
- package/lib/client-side-encryption/auto_encrypter.js +8 -5
- package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
- package/lib/client-side-encryption/client_encryption.js +48 -18
- package/lib/client-side-encryption/client_encryption.js.map +1 -1
- package/lib/client-side-encryption/state_machine.js +43 -29
- package/lib/client-side-encryption/state_machine.js.map +1 -1
- package/lib/cmap/auth/mongo_credentials.js +5 -2
- package/lib/cmap/auth/mongo_credentials.js.map +1 -1
- package/lib/cmap/auth/mongodb_aws.js +1 -1
- package/lib/cmap/auth/mongodb_aws.js.map +1 -1
- package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js +38 -0
- package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js.map +1 -0
- package/lib/cmap/auth/mongodb_oidc.js +2 -0
- package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
- package/lib/cmap/connect.js +13 -1
- package/lib/cmap/connect.js.map +1 -1
- package/lib/cmap/connection.js +75 -17
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +14 -12
- package/lib/cmap/connection_pool.js.map +1 -1
- package/lib/cmap/wire_protocol/on_data.js +5 -1
- package/lib/cmap/wire_protocol/on_data.js.map +1 -1
- package/lib/cmap/wire_protocol/responses.js +30 -0
- package/lib/cmap/wire_protocol/responses.js.map +1 -1
- package/lib/collection.js +62 -3
- package/lib/collection.js.map +1 -1
- package/lib/connection_string.js +2 -0
- package/lib/connection_string.js.map +1 -1
- package/lib/cursor/abstract_cursor.js +221 -38
- package/lib/cursor/abstract_cursor.js.map +1 -1
- package/lib/cursor/aggregation_cursor.js +29 -7
- package/lib/cursor/aggregation_cursor.js.map +1 -1
- package/lib/cursor/change_stream_cursor.js +2 -2
- package/lib/cursor/change_stream_cursor.js.map +1 -1
- package/lib/cursor/client_bulk_write_cursor.js +1 -1
- package/lib/cursor/client_bulk_write_cursor.js.map +1 -1
- package/lib/cursor/find_cursor.js +18 -8
- package/lib/cursor/find_cursor.js.map +1 -1
- package/lib/cursor/list_collections_cursor.js +1 -1
- package/lib/cursor/list_collections_cursor.js.map +1 -1
- package/lib/cursor/list_indexes_cursor.js +1 -1
- package/lib/cursor/list_indexes_cursor.js.map +1 -1
- package/lib/cursor/run_command_cursor.js +6 -4
- package/lib/cursor/run_command_cursor.js.map +1 -1
- package/lib/db.js +63 -3
- package/lib/db.js.map +1 -1
- package/lib/error.js +34 -9
- package/lib/error.js.map +1 -1
- package/lib/explain.js +57 -1
- package/lib/explain.js.map +1 -1
- package/lib/gridfs/download.js +31 -3
- package/lib/gridfs/download.js.map +1 -1
- package/lib/gridfs/index.js +49 -14
- package/lib/gridfs/index.js.map +1 -1
- package/lib/gridfs/upload.js +80 -22
- package/lib/gridfs/upload.js.map +1 -1
- package/lib/index.js +9 -5
- package/lib/index.js.map +1 -1
- package/lib/mongo_client.js +70 -1
- package/lib/mongo_client.js.map +1 -1
- package/lib/operations/aggregate.js +2 -2
- package/lib/operations/aggregate.js.map +1 -1
- package/lib/operations/bulk_write.js +7 -2
- package/lib/operations/bulk_write.js.map +1 -1
- package/lib/operations/client_bulk_write/client_bulk_write.js +3 -3
- package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -1
- package/lib/operations/client_bulk_write/executor.js +14 -3
- package/lib/operations/client_bulk_write/executor.js.map +1 -1
- package/lib/operations/command.js +5 -2
- package/lib/operations/command.js.map +1 -1
- package/lib/operations/count.js +2 -2
- package/lib/operations/count.js.map +1 -1
- package/lib/operations/create_collection.js +8 -7
- package/lib/operations/create_collection.js.map +1 -1
- package/lib/operations/delete.js +6 -6
- package/lib/operations/delete.js.map +1 -1
- package/lib/operations/distinct.js +2 -2
- package/lib/operations/distinct.js.map +1 -1
- package/lib/operations/drop.js +8 -8
- package/lib/operations/drop.js.map +1 -1
- package/lib/operations/estimated_document_count.js +2 -2
- package/lib/operations/estimated_document_count.js.map +1 -1
- package/lib/operations/execute_operation.js +16 -10
- package/lib/operations/execute_operation.js.map +1 -1
- package/lib/operations/find.js +6 -3
- package/lib/operations/find.js.map +1 -1
- package/lib/operations/find_and_modify.js +2 -2
- package/lib/operations/find_and_modify.js.map +1 -1
- package/lib/operations/get_more.js +2 -1
- package/lib/operations/get_more.js.map +1 -1
- package/lib/operations/indexes.js +6 -6
- package/lib/operations/indexes.js.map +1 -1
- package/lib/operations/insert.js +6 -6
- package/lib/operations/insert.js.map +1 -1
- package/lib/operations/kill_cursors.js +5 -2
- package/lib/operations/kill_cursors.js.map +1 -1
- package/lib/operations/list_collections.js +2 -2
- package/lib/operations/list_collections.js.map +1 -1
- package/lib/operations/list_databases.js +2 -2
- package/lib/operations/list_databases.js.map +1 -1
- package/lib/operations/operation.js.map +1 -1
- package/lib/operations/profiling_level.js +2 -2
- package/lib/operations/profiling_level.js.map +1 -1
- package/lib/operations/remove_user.js +2 -2
- package/lib/operations/remove_user.js.map +1 -1
- package/lib/operations/rename.js +2 -2
- package/lib/operations/rename.js.map +1 -1
- package/lib/operations/run_command.js +6 -4
- package/lib/operations/run_command.js.map +1 -1
- package/lib/operations/search_indexes/create.js +5 -2
- package/lib/operations/search_indexes/create.js.map +1 -1
- package/lib/operations/search_indexes/drop.js +2 -2
- package/lib/operations/search_indexes/drop.js.map +1 -1
- package/lib/operations/search_indexes/update.js +2 -2
- package/lib/operations/search_indexes/update.js.map +1 -1
- package/lib/operations/set_profiling_level.js +2 -2
- package/lib/operations/set_profiling_level.js.map +1 -1
- package/lib/operations/stats.js +2 -2
- package/lib/operations/stats.js.map +1 -1
- package/lib/operations/update.js +8 -8
- package/lib/operations/update.js.map +1 -1
- package/lib/operations/validate_collection.js +2 -2
- package/lib/operations/validate_collection.js.map +1 -1
- package/lib/read_concern.js +1 -1
- package/lib/sdam/common.js +0 -7
- package/lib/sdam/common.js.map +1 -1
- package/lib/sdam/server.js +4 -1
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +4 -2
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/server_selection.js +5 -2
- package/lib/sdam/server_selection.js.map +1 -1
- package/lib/sdam/topology.js +38 -15
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sessions.js +157 -98
- package/lib/sessions.js.map +1 -1
- package/lib/timeout.js +231 -16
- package/lib/timeout.js.map +1 -1
- package/lib/utils.js +36 -19
- package/lib/utils.js.map +1 -1
- package/lib/write_concern.js.map +1 -1
- package/mongodb.d.ts +562 -45
- package/package.json +18 -17
- package/src/admin.ts +6 -2
- package/src/bulk/common.ts +17 -5
- package/src/change_stream.ts +127 -52
- package/src/client-side-encryption/auto_encrypter.ts +12 -5
- package/src/client-side-encryption/client_encryption.ts +103 -20
- package/src/client-side-encryption/state_machine.ts +66 -32
- package/src/cmap/auth/mongo_credentials.ts +6 -3
- package/src/cmap/auth/mongodb_aws.ts +1 -1
- package/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts +38 -0
- package/src/cmap/auth/mongodb_oidc.ts +3 -1
- package/src/cmap/connect.ts +18 -1
- package/src/cmap/connection.ts +105 -17
- package/src/cmap/connection_pool.ts +15 -17
- package/src/cmap/handshake/client_metadata.ts +1 -1
- package/src/cmap/wire_protocol/on_data.ts +11 -1
- package/src/cmap/wire_protocol/responses.ts +35 -1
- package/src/collection.ts +81 -9
- package/src/connection_string.ts +2 -0
- package/src/cursor/abstract_cursor.ts +287 -39
- package/src/cursor/aggregation_cursor.ts +54 -8
- package/src/cursor/change_stream_cursor.ts +6 -2
- package/src/cursor/client_bulk_write_cursor.ts +6 -2
- package/src/cursor/find_cursor.ts +40 -9
- package/src/cursor/list_collections_cursor.ts +1 -1
- package/src/cursor/list_indexes_cursor.ts +1 -1
- package/src/cursor/run_command_cursor.ts +50 -5
- package/src/db.ts +75 -7
- package/src/error.ts +33 -8
- package/src/explain.ts +85 -0
- package/src/gridfs/download.ts +43 -4
- package/src/gridfs/index.ts +64 -16
- package/src/gridfs/upload.ts +153 -45
- package/src/index.ts +27 -5
- package/src/mongo_client.ts +76 -4
- package/src/operations/aggregate.ts +10 -2
- package/src/operations/bulk_write.ts +9 -2
- package/src/operations/client_bulk_write/client_bulk_write.ts +11 -3
- package/src/operations/client_bulk_write/executor.ts +15 -3
- package/src/operations/command.ts +18 -8
- package/src/operations/count.ts +10 -3
- package/src/operations/create_collection.ts +14 -7
- package/src/operations/delete.ts +15 -6
- package/src/operations/distinct.ts +7 -2
- package/src/operations/drop.ts +18 -8
- package/src/operations/estimated_document_count.ts +7 -2
- package/src/operations/execute_operation.ts +22 -13
- package/src/operations/find.ts +17 -5
- package/src/operations/find_and_modify.ts +7 -2
- package/src/operations/get_more.ts +4 -1
- package/src/operations/indexes.ts +20 -7
- package/src/operations/insert.ts +13 -6
- package/src/operations/kill_cursors.ts +10 -2
- package/src/operations/list_collections.ts +10 -1
- package/src/operations/list_databases.ts +9 -2
- package/src/operations/operation.ts +16 -2
- package/src/operations/profiling_level.ts +7 -2
- package/src/operations/remove_user.ts +7 -2
- package/src/operations/rename.ts +7 -2
- package/src/operations/run_command.ts +23 -4
- package/src/operations/search_indexes/create.ts +10 -2
- package/src/operations/search_indexes/drop.ts +7 -2
- package/src/operations/search_indexes/update.ts +7 -2
- package/src/operations/set_profiling_level.ts +4 -2
- package/src/operations/stats.ts +7 -2
- package/src/operations/update.ts +16 -8
- package/src/operations/validate_collection.ts +7 -2
- package/src/read_concern.ts +1 -1
- package/src/sdam/common.ts +0 -11
- package/src/sdam/server.ts +14 -4
- package/src/sdam/server_description.ts +6 -2
- package/src/sdam/server_selection.ts +5 -2
- package/src/sdam/topology.ts +43 -27
- package/src/sessions.ts +206 -120
- package/src/timeout.ts +327 -23
- package/src/transactions.ts +1 -1
- package/src/utils.ts +47 -30
- package/src/write_concern.ts +6 -3
|
@@ -21,6 +21,7 @@ import { ReadPreference, type ReadPreferenceLike } from '../read_preference';
|
|
|
21
21
|
import { type AsyncDisposable, configureResourceManagement } from '../resource_management';
|
|
22
22
|
import type { Server } from '../sdam/server';
|
|
23
23
|
import { ClientSession, maybeClearPinnedConnection } from '../sessions';
|
|
24
|
+
import { type CSOTTimeoutContext, type Timeout, TimeoutContext } from '../timeout';
|
|
24
25
|
import { type MongoDBNamespace, squashError } from '../utils';
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -60,6 +61,46 @@ export interface CursorStreamOptions {
|
|
|
60
61
|
/** @public */
|
|
61
62
|
export type CursorFlag = (typeof CURSOR_FLAGS)[number];
|
|
62
63
|
|
|
64
|
+
/**
|
|
65
|
+
* @public
|
|
66
|
+
* @experimental
|
|
67
|
+
* Specifies how `timeoutMS` is applied to the cursor. Can be either `'cursorLifeTime'` or `'iteration'`
|
|
68
|
+
* When set to `'iteration'`, the deadline specified by `timeoutMS` applies to each call of
|
|
69
|
+
* `cursor.next()`.
|
|
70
|
+
* When set to `'cursorLifetime'`, the deadline applies to the life of the entire cursor.
|
|
71
|
+
*
|
|
72
|
+
* Depending on the type of cursor being used, this option has different default values.
|
|
73
|
+
* For non-tailable cursors, this value defaults to `'cursorLifetime'`
|
|
74
|
+
* For tailable cursors, this value defaults to `'iteration'` since tailable cursors, by
|
|
75
|
+
* definition can have an arbitrarily long lifetime.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const cursor = collection.find({}, {timeoutMS: 100, timeoutMode: 'iteration'});
|
|
80
|
+
* for await (const doc of cursor) {
|
|
81
|
+
* // process doc
|
|
82
|
+
* // This will throw a timeout error if any of the iterator's `next()` calls takes more than 100ms, but
|
|
83
|
+
* // will continue to iterate successfully otherwise, regardless of the number of batches.
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* const cursor = collection.find({}, { timeoutMS: 1000, timeoutMode: 'cursorLifetime' });
|
|
90
|
+
* const docs = await cursor.toArray(); // This entire line will throw a timeout error if all batches are not fetched and returned within 1000ms.
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export const CursorTimeoutMode = Object.freeze({
|
|
94
|
+
ITERATION: 'iteration',
|
|
95
|
+
LIFETIME: 'cursorLifetime'
|
|
96
|
+
} as const);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @public
|
|
100
|
+
* @experimental
|
|
101
|
+
*/
|
|
102
|
+
export type CursorTimeoutMode = (typeof CursorTimeoutMode)[keyof typeof CursorTimeoutMode];
|
|
103
|
+
|
|
63
104
|
/** @public */
|
|
64
105
|
export interface AbstractCursorOptions extends BSONSerializeOptions {
|
|
65
106
|
session?: ClientSession;
|
|
@@ -103,8 +144,46 @@ export interface AbstractCursorOptions extends BSONSerializeOptions {
|
|
|
103
144
|
*/
|
|
104
145
|
awaitData?: boolean;
|
|
105
146
|
noCursorTimeout?: boolean;
|
|
106
|
-
/** @
|
|
147
|
+
/** Specifies the time an operation will run until it throws a timeout error. See {@link AbstractCursorOptions.timeoutMode} for more details on how this option applies to cursors. */
|
|
107
148
|
timeoutMS?: number;
|
|
149
|
+
/**
|
|
150
|
+
* @public
|
|
151
|
+
* @experimental
|
|
152
|
+
* Specifies how `timeoutMS` is applied to the cursor. Can be either `'cursorLifeTime'` or `'iteration'`
|
|
153
|
+
* When set to `'iteration'`, the deadline specified by `timeoutMS` applies to each call of
|
|
154
|
+
* `cursor.next()`.
|
|
155
|
+
* When set to `'cursorLifetime'`, the deadline applies to the life of the entire cursor.
|
|
156
|
+
*
|
|
157
|
+
* Depending on the type of cursor being used, this option has different default values.
|
|
158
|
+
* For non-tailable cursors, this value defaults to `'cursorLifetime'`
|
|
159
|
+
* For tailable cursors, this value defaults to `'iteration'` since tailable cursors, by
|
|
160
|
+
* definition can have an arbitrarily long lifetime.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* const cursor = collection.find({}, {timeoutMS: 100, timeoutMode: 'iteration'});
|
|
165
|
+
* for await (const doc of cursor) {
|
|
166
|
+
* // process doc
|
|
167
|
+
* // This will throw a timeout error if any of the iterator's `next()` calls takes more than 100ms, but
|
|
168
|
+
* // will continue to iterate successfully otherwise, regardless of the number of batches.
|
|
169
|
+
* }
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* const cursor = collection.find({}, { timeoutMS: 1000, timeoutMode: 'cursorLifetime' });
|
|
175
|
+
* const docs = await cursor.toArray(); // This entire line will throw a timeout error if all batches are not fetched and returned within 1000ms.
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
timeoutMode?: CursorTimeoutMode;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* @internal
|
|
182
|
+
*
|
|
183
|
+
* A timeout context to govern the total time the cursor can live. If provided, the cursor
|
|
184
|
+
* cannot be used in ITERATION mode.
|
|
185
|
+
*/
|
|
186
|
+
timeoutContext?: CursorTimeoutContext;
|
|
108
187
|
}
|
|
109
188
|
|
|
110
189
|
/** @internal */
|
|
@@ -117,6 +196,8 @@ export type InternalAbstractCursorOptions = Omit<AbstractCursorOptions, 'readPre
|
|
|
117
196
|
oplogReplay?: boolean;
|
|
118
197
|
exhaust?: boolean;
|
|
119
198
|
partial?: boolean;
|
|
199
|
+
|
|
200
|
+
omitMaxTimeMS?: boolean;
|
|
120
201
|
};
|
|
121
202
|
|
|
122
203
|
/** @public */
|
|
@@ -146,7 +227,11 @@ export abstract class AbstractCursor<
|
|
|
146
227
|
private cursorClient: MongoClient;
|
|
147
228
|
/** @internal */
|
|
148
229
|
private transform?: (doc: TSchema) => any;
|
|
149
|
-
/**
|
|
230
|
+
/**
|
|
231
|
+
* @internal
|
|
232
|
+
* This is true whether or not the first command fails. It only indicates whether or not the first
|
|
233
|
+
* command has been run.
|
|
234
|
+
*/
|
|
150
235
|
private initialized: boolean;
|
|
151
236
|
/** @internal */
|
|
152
237
|
private isClosed: boolean;
|
|
@@ -154,6 +239,8 @@ export abstract class AbstractCursor<
|
|
|
154
239
|
private isKilled: boolean;
|
|
155
240
|
/** @internal */
|
|
156
241
|
protected readonly cursorOptions: InternalAbstractCursorOptions;
|
|
242
|
+
/** @internal */
|
|
243
|
+
protected timeoutContext?: CursorTimeoutContext;
|
|
157
244
|
|
|
158
245
|
/** @event */
|
|
159
246
|
static readonly CLOSE = 'close' as const;
|
|
@@ -183,9 +270,50 @@ export abstract class AbstractCursor<
|
|
|
183
270
|
options.readPreference && options.readPreference instanceof ReadPreference
|
|
184
271
|
? options.readPreference
|
|
185
272
|
: ReadPreference.primary,
|
|
186
|
-
...pluckBSONSerializeOptions(options)
|
|
273
|
+
...pluckBSONSerializeOptions(options),
|
|
274
|
+
timeoutMS: options?.timeoutContext?.csotEnabled()
|
|
275
|
+
? options.timeoutContext.timeoutMS
|
|
276
|
+
: options.timeoutMS,
|
|
277
|
+
tailable: options.tailable,
|
|
278
|
+
awaitData: options.awaitData
|
|
187
279
|
};
|
|
188
|
-
|
|
280
|
+
|
|
281
|
+
if (this.cursorOptions.timeoutMS != null) {
|
|
282
|
+
if (options.timeoutMode == null) {
|
|
283
|
+
if (options.tailable) {
|
|
284
|
+
if (options.awaitData) {
|
|
285
|
+
if (
|
|
286
|
+
options.maxAwaitTimeMS != null &&
|
|
287
|
+
options.maxAwaitTimeMS >= this.cursorOptions.timeoutMS
|
|
288
|
+
)
|
|
289
|
+
throw new MongoInvalidArgumentError(
|
|
290
|
+
'Cannot specify maxAwaitTimeMS >= timeoutMS for a tailable awaitData cursor'
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
this.cursorOptions.timeoutMode = CursorTimeoutMode.ITERATION;
|
|
295
|
+
} else {
|
|
296
|
+
this.cursorOptions.timeoutMode = CursorTimeoutMode.LIFETIME;
|
|
297
|
+
}
|
|
298
|
+
} else {
|
|
299
|
+
if (options.tailable && options.timeoutMode === CursorTimeoutMode.LIFETIME) {
|
|
300
|
+
throw new MongoInvalidArgumentError(
|
|
301
|
+
"Cannot set tailable cursor's timeoutMode to LIFETIME"
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
this.cursorOptions.timeoutMode = options.timeoutMode;
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
if (options.timeoutMode != null)
|
|
308
|
+
throw new MongoInvalidArgumentError('Cannot set timeoutMode without setting timeoutMS');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Set for initial command
|
|
312
|
+
this.cursorOptions.omitMaxTimeMS =
|
|
313
|
+
this.cursorOptions.timeoutMS != null &&
|
|
314
|
+
((this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION &&
|
|
315
|
+
!this.cursorOptions.tailable) ||
|
|
316
|
+
(this.cursorOptions.tailable && !this.cursorOptions.awaitData));
|
|
189
317
|
|
|
190
318
|
const readConcern = ReadConcern.fromOptions(options);
|
|
191
319
|
if (readConcern) {
|
|
@@ -222,6 +350,8 @@ export abstract class AbstractCursor<
|
|
|
222
350
|
utf8: options?.enableUtf8Validation === false ? false : true
|
|
223
351
|
}
|
|
224
352
|
};
|
|
353
|
+
|
|
354
|
+
this.timeoutContext = options.timeoutContext;
|
|
225
355
|
}
|
|
226
356
|
|
|
227
357
|
/**
|
|
@@ -400,12 +530,21 @@ export abstract class AbstractCursor<
|
|
|
400
530
|
return false;
|
|
401
531
|
}
|
|
402
532
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
533
|
+
if (this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION && this.cursorId != null) {
|
|
534
|
+
this.timeoutContext?.refresh();
|
|
535
|
+
}
|
|
536
|
+
try {
|
|
537
|
+
do {
|
|
538
|
+
if ((this.documents?.length ?? 0) !== 0) {
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
await this.fetchBatch();
|
|
542
|
+
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
|
|
543
|
+
} finally {
|
|
544
|
+
if (this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION) {
|
|
545
|
+
this.timeoutContext?.clear();
|
|
406
546
|
}
|
|
407
|
-
|
|
408
|
-
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
|
|
547
|
+
}
|
|
409
548
|
|
|
410
549
|
return false;
|
|
411
550
|
}
|
|
@@ -416,14 +555,24 @@ export abstract class AbstractCursor<
|
|
|
416
555
|
throw new MongoCursorExhaustedError();
|
|
417
556
|
}
|
|
418
557
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
558
|
+
if (this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION && this.cursorId != null) {
|
|
559
|
+
this.timeoutContext?.refresh();
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
try {
|
|
563
|
+
do {
|
|
564
|
+
const doc = this.documents?.shift(this.deserializationOptions);
|
|
565
|
+
if (doc != null) {
|
|
566
|
+
if (this.transform != null) return await this.transformDocument(doc);
|
|
567
|
+
return doc;
|
|
568
|
+
}
|
|
569
|
+
await this.fetchBatch();
|
|
570
|
+
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
|
|
571
|
+
} finally {
|
|
572
|
+
if (this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION) {
|
|
573
|
+
this.timeoutContext?.clear();
|
|
424
574
|
}
|
|
425
|
-
|
|
426
|
-
} while (!this.isDead || (this.documents?.length ?? 0) !== 0);
|
|
575
|
+
}
|
|
427
576
|
|
|
428
577
|
return null;
|
|
429
578
|
}
|
|
@@ -436,18 +585,27 @@ export abstract class AbstractCursor<
|
|
|
436
585
|
throw new MongoCursorExhaustedError();
|
|
437
586
|
}
|
|
438
587
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
if (this.transform != null) return await this.transformDocument(doc);
|
|
442
|
-
return doc;
|
|
588
|
+
if (this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION && this.cursorId != null) {
|
|
589
|
+
this.timeoutContext?.refresh();
|
|
443
590
|
}
|
|
591
|
+
try {
|
|
592
|
+
let doc = this.documents?.shift(this.deserializationOptions);
|
|
593
|
+
if (doc != null) {
|
|
594
|
+
if (this.transform != null) return await this.transformDocument(doc);
|
|
595
|
+
return doc;
|
|
596
|
+
}
|
|
444
597
|
|
|
445
|
-
|
|
598
|
+
await this.fetchBatch();
|
|
446
599
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
600
|
+
doc = this.documents?.shift(this.deserializationOptions);
|
|
601
|
+
if (doc != null) {
|
|
602
|
+
if (this.transform != null) return await this.transformDocument(doc);
|
|
603
|
+
return doc;
|
|
604
|
+
}
|
|
605
|
+
} finally {
|
|
606
|
+
if (this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION) {
|
|
607
|
+
this.timeoutContext?.clear();
|
|
608
|
+
}
|
|
451
609
|
}
|
|
452
610
|
|
|
453
611
|
return null;
|
|
@@ -476,8 +634,8 @@ export abstract class AbstractCursor<
|
|
|
476
634
|
/**
|
|
477
635
|
* Frees any client-side resources used by the cursor.
|
|
478
636
|
*/
|
|
479
|
-
async close(): Promise<void> {
|
|
480
|
-
await this.cleanup();
|
|
637
|
+
async close(options?: { timeoutMS?: number }): Promise<void> {
|
|
638
|
+
await this.cleanup(options?.timeoutMS);
|
|
481
639
|
}
|
|
482
640
|
|
|
483
641
|
/**
|
|
@@ -652,12 +810,17 @@ export abstract class AbstractCursor<
|
|
|
652
810
|
* if the resultant data has already been retrieved by this cursor.
|
|
653
811
|
*/
|
|
654
812
|
rewind(): void {
|
|
813
|
+
if (this.timeoutContext && this.timeoutContext.owner !== this) {
|
|
814
|
+
throw new MongoAPIError(`Cannot rewind cursor that does not own its timeout context.`);
|
|
815
|
+
}
|
|
655
816
|
if (!this.initialized) {
|
|
656
817
|
return;
|
|
657
818
|
}
|
|
658
819
|
|
|
659
820
|
this.cursorId = null;
|
|
660
821
|
this.documents?.clear();
|
|
822
|
+
this.timeoutContext?.clear();
|
|
823
|
+
this.timeoutContext = undefined;
|
|
661
824
|
this.isClosed = false;
|
|
662
825
|
this.isKilled = false;
|
|
663
826
|
this.initialized = false;
|
|
@@ -696,18 +859,20 @@ export abstract class AbstractCursor<
|
|
|
696
859
|
'Unexpected null selectedServer. A cursor creating command should have set this'
|
|
697
860
|
);
|
|
698
861
|
}
|
|
862
|
+
const getMoreOptions = {
|
|
863
|
+
...this.cursorOptions,
|
|
864
|
+
session: this.cursorSession,
|
|
865
|
+
batchSize
|
|
866
|
+
};
|
|
867
|
+
|
|
699
868
|
const getMoreOperation = new GetMoreOperation(
|
|
700
869
|
this.cursorNamespace,
|
|
701
870
|
this.cursorId,
|
|
702
871
|
this.selectedServer,
|
|
703
|
-
|
|
704
|
-
...this.cursorOptions,
|
|
705
|
-
session: this.cursorSession,
|
|
706
|
-
batchSize
|
|
707
|
-
}
|
|
872
|
+
getMoreOptions
|
|
708
873
|
);
|
|
709
874
|
|
|
710
|
-
return await executeOperation(this.cursorClient, getMoreOperation);
|
|
875
|
+
return await executeOperation(this.cursorClient, getMoreOperation, this.timeoutContext);
|
|
711
876
|
}
|
|
712
877
|
|
|
713
878
|
/**
|
|
@@ -718,8 +883,19 @@ export abstract class AbstractCursor<
|
|
|
718
883
|
* a significant refactor.
|
|
719
884
|
*/
|
|
720
885
|
private async cursorInit(): Promise<void> {
|
|
886
|
+
if (this.cursorOptions.timeoutMS != null) {
|
|
887
|
+
this.timeoutContext ??= new CursorTimeoutContext(
|
|
888
|
+
TimeoutContext.create({
|
|
889
|
+
serverSelectionTimeoutMS: this.client.s.options.serverSelectionTimeoutMS,
|
|
890
|
+
timeoutMS: this.cursorOptions.timeoutMS
|
|
891
|
+
}),
|
|
892
|
+
this
|
|
893
|
+
);
|
|
894
|
+
}
|
|
721
895
|
try {
|
|
722
896
|
const state = await this._initialize(this.cursorSession);
|
|
897
|
+
// Set omitMaxTimeMS to the value needed for subsequent getMore calls
|
|
898
|
+
this.cursorOptions.omitMaxTimeMS = this.cursorOptions.timeoutMS != null;
|
|
723
899
|
const response = state.response;
|
|
724
900
|
this.selectedServer = state.server;
|
|
725
901
|
this.cursorId = response.id;
|
|
@@ -729,7 +905,7 @@ export abstract class AbstractCursor<
|
|
|
729
905
|
} catch (error) {
|
|
730
906
|
// the cursor is now initialized, even if an error occurred
|
|
731
907
|
this.initialized = true;
|
|
732
|
-
await this.cleanup(error);
|
|
908
|
+
await this.cleanup(undefined, error);
|
|
733
909
|
throw error;
|
|
734
910
|
}
|
|
735
911
|
|
|
@@ -770,10 +946,10 @@ export abstract class AbstractCursor<
|
|
|
770
946
|
this.documents = response;
|
|
771
947
|
} catch (error) {
|
|
772
948
|
try {
|
|
773
|
-
await this.cleanup(error);
|
|
774
|
-
} catch (
|
|
949
|
+
await this.cleanup(undefined, error);
|
|
950
|
+
} catch (cleanupError) {
|
|
775
951
|
// `cleanupCursor` should never throw, squash and throw the original error
|
|
776
|
-
squashError(
|
|
952
|
+
squashError(cleanupError);
|
|
777
953
|
}
|
|
778
954
|
throw error;
|
|
779
955
|
}
|
|
@@ -791,9 +967,23 @@ export abstract class AbstractCursor<
|
|
|
791
967
|
}
|
|
792
968
|
|
|
793
969
|
/** @internal */
|
|
794
|
-
private async cleanup(error?: Error) {
|
|
970
|
+
private async cleanup(timeoutMS?: number, error?: Error) {
|
|
795
971
|
this.isClosed = true;
|
|
796
972
|
const session = this.cursorSession;
|
|
973
|
+
const timeoutContextForKillCursors = (): CursorTimeoutContext | undefined => {
|
|
974
|
+
if (timeoutMS != null) {
|
|
975
|
+
this.timeoutContext?.clear();
|
|
976
|
+
return new CursorTimeoutContext(
|
|
977
|
+
TimeoutContext.create({
|
|
978
|
+
serverSelectionTimeoutMS: this.client.s.options.serverSelectionTimeoutMS,
|
|
979
|
+
timeoutMS
|
|
980
|
+
}),
|
|
981
|
+
this
|
|
982
|
+
);
|
|
983
|
+
} else {
|
|
984
|
+
return this.timeoutContext?.refreshed();
|
|
985
|
+
}
|
|
986
|
+
};
|
|
797
987
|
try {
|
|
798
988
|
if (
|
|
799
989
|
!this.isKilled &&
|
|
@@ -806,11 +996,13 @@ export abstract class AbstractCursor<
|
|
|
806
996
|
this.isKilled = true;
|
|
807
997
|
const cursorId = this.cursorId;
|
|
808
998
|
this.cursorId = Long.ZERO;
|
|
999
|
+
|
|
809
1000
|
await executeOperation(
|
|
810
1001
|
this.cursorClient,
|
|
811
1002
|
new KillCursorsOperation(cursorId, this.cursorNamespace, this.selectedServer, {
|
|
812
1003
|
session
|
|
813
|
-
})
|
|
1004
|
+
}),
|
|
1005
|
+
timeoutContextForKillCursors()
|
|
814
1006
|
);
|
|
815
1007
|
}
|
|
816
1008
|
} catch (error) {
|
|
@@ -952,3 +1144,59 @@ class ReadableCursorStream extends Readable {
|
|
|
952
1144
|
}
|
|
953
1145
|
|
|
954
1146
|
configureResourceManagement(AbstractCursor.prototype);
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* @internal
|
|
1150
|
+
* The cursor timeout context is a wrapper around a timeout context
|
|
1151
|
+
* that keeps track of the "owner" of the cursor. For timeout contexts
|
|
1152
|
+
* instantiated inside a cursor, the owner will be the cursor.
|
|
1153
|
+
*
|
|
1154
|
+
* All timeout behavior is exactly the same as the wrapped timeout context's.
|
|
1155
|
+
*/
|
|
1156
|
+
export class CursorTimeoutContext extends TimeoutContext {
|
|
1157
|
+
constructor(
|
|
1158
|
+
public timeoutContext: TimeoutContext,
|
|
1159
|
+
public owner: symbol | AbstractCursor
|
|
1160
|
+
) {
|
|
1161
|
+
super();
|
|
1162
|
+
}
|
|
1163
|
+
override get serverSelectionTimeout(): Timeout | null {
|
|
1164
|
+
return this.timeoutContext.serverSelectionTimeout;
|
|
1165
|
+
}
|
|
1166
|
+
override get connectionCheckoutTimeout(): Timeout | null {
|
|
1167
|
+
return this.timeoutContext.connectionCheckoutTimeout;
|
|
1168
|
+
}
|
|
1169
|
+
override get clearServerSelectionTimeout(): boolean {
|
|
1170
|
+
return this.timeoutContext.clearServerSelectionTimeout;
|
|
1171
|
+
}
|
|
1172
|
+
override get timeoutForSocketWrite(): Timeout | null {
|
|
1173
|
+
return this.timeoutContext.timeoutForSocketWrite;
|
|
1174
|
+
}
|
|
1175
|
+
override get timeoutForSocketRead(): Timeout | null {
|
|
1176
|
+
return this.timeoutContext.timeoutForSocketRead;
|
|
1177
|
+
}
|
|
1178
|
+
override csotEnabled(): this is CSOTTimeoutContext {
|
|
1179
|
+
return this.timeoutContext.csotEnabled();
|
|
1180
|
+
}
|
|
1181
|
+
override refresh(): void {
|
|
1182
|
+
if (typeof this.owner !== 'symbol') return this.timeoutContext.refresh();
|
|
1183
|
+
}
|
|
1184
|
+
override clear(): void {
|
|
1185
|
+
if (typeof this.owner !== 'symbol') return this.timeoutContext.clear();
|
|
1186
|
+
}
|
|
1187
|
+
override get maxTimeMS(): number | null {
|
|
1188
|
+
return this.timeoutContext.maxTimeMS;
|
|
1189
|
+
}
|
|
1190
|
+
get timeoutMS(): number | null {
|
|
1191
|
+
return this.timeoutContext.csotEnabled() ? this.timeoutContext.timeoutMS : null;
|
|
1192
|
+
}
|
|
1193
|
+
override refreshed(): CursorTimeoutContext {
|
|
1194
|
+
return new CursorTimeoutContext(this.timeoutContext.refreshed(), this.owner);
|
|
1195
|
+
}
|
|
1196
|
+
override addMaxTimeMSToCommand(command: Document, options: { omitMaxTimeMS?: boolean }): void {
|
|
1197
|
+
this.timeoutContext.addMaxTimeMSToCommand(command, options);
|
|
1198
|
+
}
|
|
1199
|
+
override getSocketTimeoutMS(): number | undefined {
|
|
1200
|
+
return this.timeoutContext.getSocketTimeoutMS();
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { Document } from '../bson';
|
|
2
|
-
import
|
|
2
|
+
import { MongoAPIError } from '../error';
|
|
3
|
+
import {
|
|
4
|
+
Explain,
|
|
5
|
+
ExplainableCursor,
|
|
6
|
+
type ExplainCommandOptions,
|
|
7
|
+
type ExplainVerbosityLike,
|
|
8
|
+
validateExplainTimeoutOptions
|
|
9
|
+
} from '../explain';
|
|
3
10
|
import type { MongoClient } from '../mongo_client';
|
|
4
11
|
import { AggregateOperation, type AggregateOptions } from '../operations/aggregate';
|
|
5
12
|
import { executeOperation } from '../operations/execute_operation';
|
|
@@ -7,8 +14,8 @@ import type { ClientSession } from '../sessions';
|
|
|
7
14
|
import type { Sort } from '../sort';
|
|
8
15
|
import { mergeOptions, type MongoDBNamespace } from '../utils';
|
|
9
16
|
import {
|
|
10
|
-
AbstractCursor,
|
|
11
17
|
type AbstractCursorOptions,
|
|
18
|
+
CursorTimeoutMode,
|
|
12
19
|
type InitialCursorResponse
|
|
13
20
|
} from './abstract_cursor';
|
|
14
21
|
|
|
@@ -22,7 +29,7 @@ export interface AggregationCursorOptions extends AbstractCursorOptions, Aggrega
|
|
|
22
29
|
* or higher stream
|
|
23
30
|
* @public
|
|
24
31
|
*/
|
|
25
|
-
export class AggregationCursor<TSchema = any> extends
|
|
32
|
+
export class AggregationCursor<TSchema = any> extends ExplainableCursor<TSchema> {
|
|
26
33
|
public readonly pipeline: Document[];
|
|
27
34
|
/** @internal */
|
|
28
35
|
private aggregateOptions: AggregateOptions;
|
|
@@ -38,6 +45,15 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
|
|
|
38
45
|
|
|
39
46
|
this.pipeline = pipeline;
|
|
40
47
|
this.aggregateOptions = options;
|
|
48
|
+
|
|
49
|
+
const lastStage: Document | undefined = this.pipeline[this.pipeline.length - 1];
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
this.cursorOptions.timeoutMS != null &&
|
|
53
|
+
this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION &&
|
|
54
|
+
(lastStage?.$merge != null || lastStage?.$out != null)
|
|
55
|
+
)
|
|
56
|
+
throw new MongoAPIError('Cannot use $out or $merge stage with ITERATION timeoutMode');
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
clone(): AggregationCursor<TSchema> {
|
|
@@ -54,26 +70,49 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
|
|
|
54
70
|
|
|
55
71
|
/** @internal */
|
|
56
72
|
async _initialize(session: ClientSession): Promise<InitialCursorResponse> {
|
|
57
|
-
const
|
|
73
|
+
const options = {
|
|
58
74
|
...this.aggregateOptions,
|
|
59
75
|
...this.cursorOptions,
|
|
60
76
|
session
|
|
61
|
-
}
|
|
77
|
+
};
|
|
78
|
+
if (options.explain) {
|
|
79
|
+
try {
|
|
80
|
+
validateExplainTimeoutOptions(options, Explain.fromOptions(options));
|
|
81
|
+
} catch {
|
|
82
|
+
throw new MongoAPIError(
|
|
83
|
+
'timeoutMS cannot be used with explain when explain is specified in aggregateOptions'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const aggregateOperation = new AggregateOperation(this.namespace, this.pipeline, options);
|
|
62
89
|
|
|
63
|
-
const response = await executeOperation(this.client, aggregateOperation);
|
|
90
|
+
const response = await executeOperation(this.client, aggregateOperation, this.timeoutContext);
|
|
64
91
|
|
|
65
92
|
return { server: aggregateOperation.server, session, response };
|
|
66
93
|
}
|
|
67
94
|
|
|
68
95
|
/** Execute the explain for the cursor */
|
|
69
|
-
async explain(
|
|
96
|
+
async explain(): Promise<Document>;
|
|
97
|
+
async explain(verbosity: ExplainVerbosityLike | ExplainCommandOptions): Promise<Document>;
|
|
98
|
+
async explain(options: { timeoutMS?: number }): Promise<Document>;
|
|
99
|
+
async explain(
|
|
100
|
+
verbosity: ExplainVerbosityLike | ExplainCommandOptions,
|
|
101
|
+
options: { timeoutMS?: number }
|
|
102
|
+
): Promise<Document>;
|
|
103
|
+
async explain(
|
|
104
|
+
verbosity?: ExplainVerbosityLike | ExplainCommandOptions | { timeoutMS?: number },
|
|
105
|
+
options?: { timeoutMS?: number }
|
|
106
|
+
): Promise<Document> {
|
|
107
|
+
const { explain, timeout } = this.resolveExplainTimeoutOptions(verbosity, options);
|
|
70
108
|
return (
|
|
71
109
|
await executeOperation(
|
|
72
110
|
this.client,
|
|
73
111
|
new AggregateOperation(this.namespace, this.pipeline, {
|
|
74
112
|
...this.aggregateOptions, // NOTE: order matters here, we may need to refine this
|
|
75
113
|
...this.cursorOptions,
|
|
76
|
-
|
|
114
|
+
...timeout,
|
|
115
|
+
explain: explain ?? true
|
|
77
116
|
})
|
|
78
117
|
)
|
|
79
118
|
).shift(this.deserializationOptions);
|
|
@@ -95,6 +134,13 @@ export class AggregationCursor<TSchema = any> extends AbstractCursor<TSchema> {
|
|
|
95
134
|
addStage<T = Document>(stage: Document): AggregationCursor<T>;
|
|
96
135
|
addStage<T = Document>(stage: Document): AggregationCursor<T> {
|
|
97
136
|
this.throwIfInitialized();
|
|
137
|
+
if (
|
|
138
|
+
this.cursorOptions.timeoutMS != null &&
|
|
139
|
+
this.cursorOptions.timeoutMode === CursorTimeoutMode.ITERATION &&
|
|
140
|
+
(stage.$out != null || stage.$merge != null)
|
|
141
|
+
) {
|
|
142
|
+
throw new MongoAPIError('Cannot use $out or $merge stage with ITERATION timeoutMode');
|
|
143
|
+
}
|
|
98
144
|
this.pipeline.push(stage);
|
|
99
145
|
return this as unknown as AggregationCursor<T>;
|
|
100
146
|
}
|
|
@@ -55,7 +55,7 @@ export class ChangeStreamCursor<
|
|
|
55
55
|
pipeline: Document[] = [],
|
|
56
56
|
options: ChangeStreamCursorOptions = {}
|
|
57
57
|
) {
|
|
58
|
-
super(client, namespace, options);
|
|
58
|
+
super(client, namespace, { ...options, tailable: true, awaitData: true });
|
|
59
59
|
|
|
60
60
|
this.pipeline = pipeline;
|
|
61
61
|
this.changeStreamCursorOptions = options;
|
|
@@ -133,7 +133,11 @@ export class ChangeStreamCursor<
|
|
|
133
133
|
session
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
const response = await executeOperation(
|
|
136
|
+
const response = await executeOperation(
|
|
137
|
+
session.client,
|
|
138
|
+
aggregateOperation,
|
|
139
|
+
this.timeoutContext
|
|
140
|
+
);
|
|
137
141
|
|
|
138
142
|
const server = aggregateOperation.server;
|
|
139
143
|
this.maxWireVersion = maxWireVersion(server);
|
|
@@ -34,7 +34,7 @@ export class ClientBulkWriteCursor extends AbstractCursor {
|
|
|
34
34
|
constructor(
|
|
35
35
|
client: MongoClient,
|
|
36
36
|
commandBuilder: ClientBulkWriteCommandBuilder,
|
|
37
|
-
options:
|
|
37
|
+
options: ClientBulkWriteCursorOptions = {}
|
|
38
38
|
) {
|
|
39
39
|
super(client, new MongoDBNamespace('admin', '$cmd'), options);
|
|
40
40
|
|
|
@@ -71,7 +71,11 @@ export class ClientBulkWriteCursor extends AbstractCursor {
|
|
|
71
71
|
session
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
const response = await executeOperation(
|
|
74
|
+
const response = await executeOperation(
|
|
75
|
+
this.client,
|
|
76
|
+
clientBulkWriteOperation,
|
|
77
|
+
this.timeoutContext
|
|
78
|
+
);
|
|
75
79
|
this.cursorResponse = response;
|
|
76
80
|
|
|
77
81
|
return { server: clientBulkWriteOperation.server, session, response };
|