mongodb 6.10.0 → 6.11.0
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 +17 -16
- 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
package/src/sessions.ts
CHANGED
|
@@ -29,6 +29,7 @@ import { ReadConcernLevel } from './read_concern';
|
|
|
29
29
|
import { ReadPreference } from './read_preference';
|
|
30
30
|
import { type AsyncDisposable, configureResourceManagement } from './resource_management';
|
|
31
31
|
import { _advanceClusterTime, type ClusterTime, TopologyType } from './sdam/common';
|
|
32
|
+
import { TimeoutContext } from './timeout';
|
|
32
33
|
import {
|
|
33
34
|
isTransactionCommand,
|
|
34
35
|
Transaction,
|
|
@@ -58,8 +59,12 @@ export interface ClientSessionOptions {
|
|
|
58
59
|
snapshot?: boolean;
|
|
59
60
|
/** The default TransactionOptions to use for transactions started on this session. */
|
|
60
61
|
defaultTransactionOptions?: TransactionOptions;
|
|
61
|
-
/**
|
|
62
|
-
*
|
|
62
|
+
/**
|
|
63
|
+
* @public
|
|
64
|
+
* @experimental
|
|
65
|
+
* An overriding timeoutMS value to use for a client-side timeout.
|
|
66
|
+
* If not provided the session uses the timeoutMS specified on the MongoClient.
|
|
67
|
+
*/
|
|
63
68
|
defaultTimeoutMS?: number;
|
|
64
69
|
|
|
65
70
|
/** @internal */
|
|
@@ -98,6 +103,9 @@ export interface EndSessionOptions {
|
|
|
98
103
|
error?: AnyError;
|
|
99
104
|
force?: boolean;
|
|
100
105
|
forceClear?: boolean;
|
|
106
|
+
|
|
107
|
+
/** Specifies the time an operation will run until it throws a timeout error */
|
|
108
|
+
timeoutMS?: number;
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
/**
|
|
@@ -115,7 +123,7 @@ export class ClientSession
|
|
|
115
123
|
/** @internal */
|
|
116
124
|
sessionPool: ServerSessionPool;
|
|
117
125
|
hasEnded: boolean;
|
|
118
|
-
clientOptions
|
|
126
|
+
clientOptions: MongoOptions;
|
|
119
127
|
supports: { causalConsistency: boolean };
|
|
120
128
|
clusterTime?: ClusterTime;
|
|
121
129
|
operationTime?: Timestamp;
|
|
@@ -129,7 +137,7 @@ export class ClientSession
|
|
|
129
137
|
* initially undefined. Gets set to false when startTransaction is called. When commitTransaction is sent to server, if the commitTransaction succeeds, it is then set to undefined, otherwise, set to true */
|
|
130
138
|
commitAttempted?: boolean;
|
|
131
139
|
/** @internal */
|
|
132
|
-
[kServerSession]: ServerSession | null;
|
|
140
|
+
private [kServerSession]: ServerSession | null;
|
|
133
141
|
/** @internal */
|
|
134
142
|
[kSnapshotTime]?: Timestamp;
|
|
135
143
|
/** @internal */
|
|
@@ -138,9 +146,15 @@ export class ClientSession
|
|
|
138
146
|
[kPinnedConnection]?: Connection;
|
|
139
147
|
/** @internal */
|
|
140
148
|
[kTxnNumberIncrement]: number;
|
|
141
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* @experimental
|
|
151
|
+
* Specifies the time an operation in a given `ClientSession` will run until it throws a timeout error
|
|
152
|
+
*/
|
|
142
153
|
timeoutMS?: number;
|
|
143
154
|
|
|
155
|
+
/** @internal */
|
|
156
|
+
public timeoutContext: TimeoutContext | null = null;
|
|
157
|
+
|
|
144
158
|
/**
|
|
145
159
|
* Create a client session.
|
|
146
160
|
* @internal
|
|
@@ -153,7 +167,7 @@ export class ClientSession
|
|
|
153
167
|
client: MongoClient,
|
|
154
168
|
sessionPool: ServerSessionPool,
|
|
155
169
|
options: ClientSessionOptions,
|
|
156
|
-
clientOptions
|
|
170
|
+
clientOptions: MongoOptions
|
|
157
171
|
) {
|
|
158
172
|
super();
|
|
159
173
|
|
|
@@ -273,27 +287,25 @@ export class ClientSession
|
|
|
273
287
|
async endSession(options?: EndSessionOptions): Promise<void> {
|
|
274
288
|
try {
|
|
275
289
|
if (this.inTransaction()) {
|
|
276
|
-
await this.abortTransaction();
|
|
290
|
+
await this.abortTransaction({ ...options, throwTimeout: true });
|
|
277
291
|
}
|
|
292
|
+
} catch (error) {
|
|
293
|
+
// spec indicates that we should ignore all errors for `endSessions`
|
|
294
|
+
if (error.name === 'MongoOperationTimeoutError') throw error;
|
|
295
|
+
squashError(error);
|
|
296
|
+
} finally {
|
|
278
297
|
if (!this.hasEnded) {
|
|
279
298
|
const serverSession = this[kServerSession];
|
|
280
299
|
if (serverSession != null) {
|
|
281
300
|
// release the server session back to the pool
|
|
282
301
|
this.sessionPool.release(serverSession);
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
value: ServerSession.clone(serverSession),
|
|
286
|
-
writable: false
|
|
287
|
-
});
|
|
302
|
+
// Store a clone of the server session for reference (debugging)
|
|
303
|
+
this[kServerSession] = new ServerSession(serverSession);
|
|
288
304
|
}
|
|
289
305
|
// mark the session as ended, and emit a signal
|
|
290
306
|
this.hasEnded = true;
|
|
291
307
|
this.emit('ended', this);
|
|
292
308
|
}
|
|
293
|
-
} catch (error) {
|
|
294
|
-
// spec indicates that we should ignore all errors for `endSessions`
|
|
295
|
-
squashError(error);
|
|
296
|
-
} finally {
|
|
297
309
|
maybeClearPinnedConnection(this, { force: true, ...options });
|
|
298
310
|
}
|
|
299
311
|
}
|
|
@@ -446,8 +458,10 @@ export class ClientSession
|
|
|
446
458
|
|
|
447
459
|
/**
|
|
448
460
|
* Commits the currently active transaction in this session.
|
|
461
|
+
*
|
|
462
|
+
* @param options - Optional options, can be used to override `defaultTimeoutMS`.
|
|
449
463
|
*/
|
|
450
|
-
async commitTransaction(): Promise<void> {
|
|
464
|
+
async commitTransaction(options?: { timeoutMS?: number }): Promise<void> {
|
|
451
465
|
if (this.transaction.state === TxnState.NO_TRANSACTION) {
|
|
452
466
|
throw new MongoTransactionError('No transaction started');
|
|
453
467
|
}
|
|
@@ -474,13 +488,31 @@ export class ClientSession
|
|
|
474
488
|
maxTimeMS?: number;
|
|
475
489
|
} = { commitTransaction: 1 };
|
|
476
490
|
|
|
491
|
+
const timeoutMS =
|
|
492
|
+
typeof options?.timeoutMS === 'number'
|
|
493
|
+
? options.timeoutMS
|
|
494
|
+
: typeof this.timeoutMS === 'number'
|
|
495
|
+
? this.timeoutMS
|
|
496
|
+
: null;
|
|
497
|
+
|
|
477
498
|
const wc = this.transaction.options.writeConcern ?? this.clientOptions?.writeConcern;
|
|
478
499
|
if (wc != null) {
|
|
479
|
-
|
|
500
|
+
if (timeoutMS == null && this.timeoutContext == null) {
|
|
501
|
+
WriteConcern.apply(command, { wtimeoutMS: 10000, w: 'majority', ...wc });
|
|
502
|
+
} else {
|
|
503
|
+
const wcKeys = Object.keys(wc);
|
|
504
|
+
if (wcKeys.length > 2 || (!wcKeys.includes('wtimeoutMS') && !wcKeys.includes('wTimeoutMS')))
|
|
505
|
+
// if the write concern was specified with wTimeoutMS, then we set both wtimeoutMS and wTimeoutMS, guaranteeing at least two keys, so if we have more than two keys, then we can automatically assume that we should add the write concern to the command. If it has 2 or fewer keys, we need to check that those keys aren't the wtimeoutMS or wTimeoutMS options before we add the write concern to the command
|
|
506
|
+
WriteConcern.apply(command, { ...wc, wtimeoutMS: undefined });
|
|
507
|
+
}
|
|
480
508
|
}
|
|
481
509
|
|
|
482
510
|
if (this.transaction.state === TxnState.TRANSACTION_COMMITTED || this.commitAttempted) {
|
|
483
|
-
|
|
511
|
+
if (timeoutMS == null && this.timeoutContext == null) {
|
|
512
|
+
WriteConcern.apply(command, { wtimeoutMS: 10000, ...wc, w: 'majority' });
|
|
513
|
+
} else {
|
|
514
|
+
WriteConcern.apply(command, { w: 'majority', ...wc, wtimeoutMS: undefined });
|
|
515
|
+
}
|
|
484
516
|
}
|
|
485
517
|
|
|
486
518
|
if (typeof this.transaction.options.maxTimeMS === 'number') {
|
|
@@ -497,8 +529,18 @@ export class ClientSession
|
|
|
497
529
|
bypassPinningCheck: true
|
|
498
530
|
});
|
|
499
531
|
|
|
532
|
+
const timeoutContext =
|
|
533
|
+
this.timeoutContext ??
|
|
534
|
+
(typeof timeoutMS === 'number'
|
|
535
|
+
? TimeoutContext.create({
|
|
536
|
+
serverSelectionTimeoutMS: this.clientOptions.serverSelectionTimeoutMS,
|
|
537
|
+
socketTimeoutMS: this.clientOptions.socketTimeoutMS,
|
|
538
|
+
timeoutMS
|
|
539
|
+
})
|
|
540
|
+
: null);
|
|
541
|
+
|
|
500
542
|
try {
|
|
501
|
-
await executeOperation(this.client, operation);
|
|
543
|
+
await executeOperation(this.client, operation, timeoutContext);
|
|
502
544
|
this.commitAttempted = undefined;
|
|
503
545
|
return;
|
|
504
546
|
} catch (firstCommitError) {
|
|
@@ -516,7 +558,8 @@ export class ClientSession
|
|
|
516
558
|
session: this,
|
|
517
559
|
readPreference: ReadPreference.primary,
|
|
518
560
|
bypassPinningCheck: true
|
|
519
|
-
})
|
|
561
|
+
}),
|
|
562
|
+
timeoutContext
|
|
520
563
|
);
|
|
521
564
|
return;
|
|
522
565
|
} catch (retryCommitError) {
|
|
@@ -549,8 +592,13 @@ export class ClientSession
|
|
|
549
592
|
|
|
550
593
|
/**
|
|
551
594
|
* Aborts the currently active transaction in this session.
|
|
595
|
+
*
|
|
596
|
+
* @param options - Optional options, can be used to override `defaultTimeoutMS`.
|
|
552
597
|
*/
|
|
553
|
-
async abortTransaction(): Promise<void
|
|
598
|
+
async abortTransaction(options?: { timeoutMS?: number }): Promise<void>;
|
|
599
|
+
/** @internal */
|
|
600
|
+
async abortTransaction(options?: { timeoutMS?: number; throwTimeout?: true }): Promise<void>;
|
|
601
|
+
async abortTransaction(options?: { timeoutMS?: number; throwTimeout?: true }): Promise<void> {
|
|
554
602
|
if (this.transaction.state === TxnState.NO_TRANSACTION) {
|
|
555
603
|
throw new MongoTransactionError('No transaction started');
|
|
556
604
|
}
|
|
@@ -580,8 +628,26 @@ export class ClientSession
|
|
|
580
628
|
recoveryToken?: Document;
|
|
581
629
|
} = { abortTransaction: 1 };
|
|
582
630
|
|
|
631
|
+
const timeoutMS =
|
|
632
|
+
typeof options?.timeoutMS === 'number'
|
|
633
|
+
? options.timeoutMS
|
|
634
|
+
: this.timeoutContext?.csotEnabled()
|
|
635
|
+
? this.timeoutContext.timeoutMS // refresh timeoutMS for abort operation
|
|
636
|
+
: typeof this.timeoutMS === 'number'
|
|
637
|
+
? this.timeoutMS
|
|
638
|
+
: null;
|
|
639
|
+
|
|
640
|
+
const timeoutContext =
|
|
641
|
+
timeoutMS != null
|
|
642
|
+
? TimeoutContext.create({
|
|
643
|
+
timeoutMS,
|
|
644
|
+
serverSelectionTimeoutMS: this.clientOptions.serverSelectionTimeoutMS,
|
|
645
|
+
socketTimeoutMS: this.clientOptions.socketTimeoutMS
|
|
646
|
+
})
|
|
647
|
+
: null;
|
|
648
|
+
|
|
583
649
|
const wc = this.transaction.options.writeConcern ?? this.clientOptions?.writeConcern;
|
|
584
|
-
if (wc != null) {
|
|
650
|
+
if (wc != null && timeoutMS == null) {
|
|
585
651
|
WriteConcern.apply(command, { wtimeoutMS: 10000, w: 'majority', ...wc });
|
|
586
652
|
}
|
|
587
653
|
|
|
@@ -596,17 +662,26 @@ export class ClientSession
|
|
|
596
662
|
});
|
|
597
663
|
|
|
598
664
|
try {
|
|
599
|
-
await executeOperation(this.client, operation);
|
|
665
|
+
await executeOperation(this.client, operation, timeoutContext);
|
|
600
666
|
this.unpin();
|
|
601
667
|
return;
|
|
602
668
|
} catch (firstAbortError) {
|
|
603
669
|
this.unpin();
|
|
604
670
|
|
|
671
|
+
if (firstAbortError.name === 'MongoRuntimeError') throw firstAbortError;
|
|
672
|
+
if (options?.throwTimeout && firstAbortError.name === 'MongoOperationTimeoutError') {
|
|
673
|
+
throw firstAbortError;
|
|
674
|
+
}
|
|
675
|
+
|
|
605
676
|
if (firstAbortError instanceof MongoError && isRetryableWriteError(firstAbortError)) {
|
|
606
677
|
try {
|
|
607
|
-
await executeOperation(this.client, operation);
|
|
678
|
+
await executeOperation(this.client, operation, timeoutContext);
|
|
608
679
|
return;
|
|
609
|
-
} catch {
|
|
680
|
+
} catch (secondAbortError) {
|
|
681
|
+
if (secondAbortError.name === 'MongoRuntimeError') throw secondAbortError;
|
|
682
|
+
if (options?.throwTimeout && secondAbortError.name === 'MongoOperationTimeoutError') {
|
|
683
|
+
throw secondAbortError;
|
|
684
|
+
}
|
|
610
685
|
// we do not retry the retry
|
|
611
686
|
}
|
|
612
687
|
}
|
|
@@ -636,6 +711,9 @@ export class ClientSession
|
|
|
636
711
|
* `Promise.allSettled`, `Promise.race`, etc to parallelize operations inside a transaction is
|
|
637
712
|
* undefined behaviour.
|
|
638
713
|
*
|
|
714
|
+
* **IMPORTANT:** When running an operation inside a `withTransaction` callback, if it is not
|
|
715
|
+
* provided the explicit session in its options, it will not be part of the transaction and it will not respect timeoutMS.
|
|
716
|
+
*
|
|
639
717
|
*
|
|
640
718
|
* @remarks
|
|
641
719
|
* - If all operations successfully complete and the `commitTransaction` operation is successful, then the provided function will return the result of the provided function.
|
|
@@ -661,96 +739,119 @@ export class ClientSession
|
|
|
661
739
|
*/
|
|
662
740
|
async withTransaction<T = any>(
|
|
663
741
|
fn: WithTransactionCallback<T>,
|
|
664
|
-
options?: TransactionOptions
|
|
742
|
+
options?: TransactionOptions & {
|
|
743
|
+
/**
|
|
744
|
+
* Configures a timeoutMS expiry for the entire withTransactionCallback.
|
|
745
|
+
*
|
|
746
|
+
* @remarks
|
|
747
|
+
* - The remaining timeout will not be applied to callback operations that do not use the ClientSession.
|
|
748
|
+
* - Overriding timeoutMS for operations executed using the explicit session inside the provided callback will result in a client-side error.
|
|
749
|
+
*/
|
|
750
|
+
timeoutMS?: number;
|
|
751
|
+
}
|
|
665
752
|
): Promise<T> {
|
|
666
753
|
const MAX_TIMEOUT = 120000;
|
|
667
|
-
const startTime = now();
|
|
668
754
|
|
|
669
|
-
|
|
670
|
-
|
|
755
|
+
const timeoutMS = options?.timeoutMS ?? this.timeoutMS ?? null;
|
|
756
|
+
this.timeoutContext =
|
|
757
|
+
timeoutMS != null
|
|
758
|
+
? TimeoutContext.create({
|
|
759
|
+
timeoutMS,
|
|
760
|
+
serverSelectionTimeoutMS: this.clientOptions.serverSelectionTimeoutMS,
|
|
761
|
+
socketTimeoutMS: this.clientOptions.socketTimeoutMS
|
|
762
|
+
})
|
|
763
|
+
: null;
|
|
671
764
|
|
|
672
|
-
|
|
673
|
-
this.startTransaction(options); // may throw on error
|
|
765
|
+
const startTime = this.timeoutContext?.csotEnabled() ? this.timeoutContext.start : now();
|
|
674
766
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (!isPromiseLike(promise)) {
|
|
678
|
-
throw new MongoInvalidArgumentError(
|
|
679
|
-
'Function provided to `withTransaction` must return a Promise'
|
|
680
|
-
);
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
result = await promise;
|
|
767
|
+
let committed = false;
|
|
768
|
+
let result: any;
|
|
684
769
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
this.transaction.state === TxnState.TRANSACTION_ABORTED
|
|
689
|
-
) {
|
|
690
|
-
// Assume callback intentionally ended the transaction
|
|
691
|
-
return result;
|
|
692
|
-
}
|
|
693
|
-
} catch (fnError) {
|
|
694
|
-
if (!(fnError instanceof MongoError) || fnError instanceof MongoInvalidArgumentError) {
|
|
695
|
-
await this.abortTransaction();
|
|
696
|
-
throw fnError;
|
|
697
|
-
}
|
|
770
|
+
try {
|
|
771
|
+
while (!committed) {
|
|
772
|
+
this.startTransaction(options); // may throw on error
|
|
698
773
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
774
|
+
try {
|
|
775
|
+
const promise = fn(this);
|
|
776
|
+
if (!isPromiseLike(promise)) {
|
|
777
|
+
throw new MongoInvalidArgumentError(
|
|
778
|
+
'Function provided to `withTransaction` must return a Promise'
|
|
779
|
+
);
|
|
780
|
+
}
|
|
705
781
|
|
|
706
|
-
|
|
707
|
-
fnError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
|
|
708
|
-
now() - startTime < MAX_TIMEOUT
|
|
709
|
-
) {
|
|
710
|
-
continue;
|
|
711
|
-
}
|
|
782
|
+
result = await promise;
|
|
712
783
|
|
|
713
|
-
|
|
714
|
-
|
|
784
|
+
if (
|
|
785
|
+
this.transaction.state === TxnState.NO_TRANSACTION ||
|
|
786
|
+
this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
|
|
787
|
+
this.transaction.state === TxnState.TRANSACTION_ABORTED
|
|
788
|
+
) {
|
|
789
|
+
// Assume callback intentionally ended the transaction
|
|
790
|
+
return result;
|
|
791
|
+
}
|
|
792
|
+
} catch (fnError) {
|
|
793
|
+
if (!(fnError instanceof MongoError) || fnError instanceof MongoInvalidArgumentError) {
|
|
794
|
+
await this.abortTransaction();
|
|
795
|
+
throw fnError;
|
|
796
|
+
}
|
|
715
797
|
|
|
716
|
-
while (!committed) {
|
|
717
|
-
try {
|
|
718
|
-
/*
|
|
719
|
-
* We will rely on ClientSession.commitTransaction() to
|
|
720
|
-
* apply a majority write concern if commitTransaction is
|
|
721
|
-
* being retried (see: DRIVERS-601)
|
|
722
|
-
*/
|
|
723
|
-
await this.commitTransaction();
|
|
724
|
-
committed = true;
|
|
725
|
-
} catch (commitError) {
|
|
726
|
-
/*
|
|
727
|
-
* Note: a maxTimeMS error will have the MaxTimeMSExpired
|
|
728
|
-
* code (50) and can be reported as a top-level error or
|
|
729
|
-
* inside writeConcernError, ex.
|
|
730
|
-
* { ok:0, code: 50, codeName: 'MaxTimeMSExpired' }
|
|
731
|
-
* { ok:1, writeConcernError: { code: 50, codeName: 'MaxTimeMSExpired' } }
|
|
732
|
-
*/
|
|
733
798
|
if (
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
now() - startTime < MAX_TIMEOUT
|
|
799
|
+
this.transaction.state === TxnState.STARTING_TRANSACTION ||
|
|
800
|
+
this.transaction.state === TxnState.TRANSACTION_IN_PROGRESS
|
|
737
801
|
) {
|
|
738
|
-
|
|
802
|
+
await this.abortTransaction();
|
|
739
803
|
}
|
|
740
804
|
|
|
741
805
|
if (
|
|
742
|
-
|
|
743
|
-
now() - startTime < MAX_TIMEOUT
|
|
806
|
+
fnError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
|
|
807
|
+
(this.timeoutContext != null || now() - startTime < MAX_TIMEOUT)
|
|
744
808
|
) {
|
|
745
|
-
|
|
809
|
+
continue;
|
|
746
810
|
}
|
|
747
811
|
|
|
748
|
-
throw
|
|
812
|
+
throw fnError;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
while (!committed) {
|
|
816
|
+
try {
|
|
817
|
+
/*
|
|
818
|
+
* We will rely on ClientSession.commitTransaction() to
|
|
819
|
+
* apply a majority write concern if commitTransaction is
|
|
820
|
+
* being retried (see: DRIVERS-601)
|
|
821
|
+
*/
|
|
822
|
+
await this.commitTransaction();
|
|
823
|
+
committed = true;
|
|
824
|
+
} catch (commitError) {
|
|
825
|
+
/*
|
|
826
|
+
* Note: a maxTimeMS error will have the MaxTimeMSExpired
|
|
827
|
+
* code (50) and can be reported as a top-level error or
|
|
828
|
+
* inside writeConcernError, ex.
|
|
829
|
+
* { ok:0, code: 50, codeName: 'MaxTimeMSExpired' }
|
|
830
|
+
* { ok:1, writeConcernError: { code: 50, codeName: 'MaxTimeMSExpired' } }
|
|
831
|
+
*/
|
|
832
|
+
if (
|
|
833
|
+
!isMaxTimeMSExpiredError(commitError) &&
|
|
834
|
+
commitError.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult) &&
|
|
835
|
+
(this.timeoutContext != null || now() - startTime < MAX_TIMEOUT)
|
|
836
|
+
) {
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
if (
|
|
841
|
+
commitError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
|
|
842
|
+
(this.timeoutContext != null || now() - startTime < MAX_TIMEOUT)
|
|
843
|
+
) {
|
|
844
|
+
break;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
throw commitError;
|
|
848
|
+
}
|
|
749
849
|
}
|
|
750
850
|
}
|
|
851
|
+
return result;
|
|
852
|
+
} finally {
|
|
853
|
+
this.timeoutContext = null;
|
|
751
854
|
}
|
|
752
|
-
|
|
753
|
-
return result;
|
|
754
855
|
}
|
|
755
856
|
}
|
|
756
857
|
|
|
@@ -869,7 +970,16 @@ export class ServerSession {
|
|
|
869
970
|
isDirty: boolean;
|
|
870
971
|
|
|
871
972
|
/** @internal */
|
|
872
|
-
constructor() {
|
|
973
|
+
constructor(cloned?: ServerSession | null) {
|
|
974
|
+
if (cloned != null) {
|
|
975
|
+
const idBytes = Buffer.allocUnsafe(16);
|
|
976
|
+
idBytes.set(cloned.id.id.buffer);
|
|
977
|
+
this.id = { id: new Binary(idBytes, cloned.id.id.sub_type) };
|
|
978
|
+
this.lastUse = cloned.lastUse;
|
|
979
|
+
this.txnNumber = cloned.txnNumber;
|
|
980
|
+
this.isDirty = cloned.isDirty;
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
873
983
|
this.id = { id: new Binary(uuidV4(), Binary.SUBTYPE_UUID) };
|
|
874
984
|
this.lastUse = now();
|
|
875
985
|
this.txnNumber = 0;
|
|
@@ -890,30 +1000,6 @@ export class ServerSession {
|
|
|
890
1000
|
|
|
891
1001
|
return idleTimeMinutes > sessionTimeoutMinutes - 1;
|
|
892
1002
|
}
|
|
893
|
-
|
|
894
|
-
/**
|
|
895
|
-
* @internal
|
|
896
|
-
* Cloning meant to keep a readable reference to the server session data
|
|
897
|
-
* after ClientSession has ended
|
|
898
|
-
*/
|
|
899
|
-
static clone(serverSession: ServerSession): Readonly<ServerSession> {
|
|
900
|
-
const arrayBuffer = new ArrayBuffer(16);
|
|
901
|
-
const idBytes = Buffer.from(arrayBuffer);
|
|
902
|
-
idBytes.set(serverSession.id.id.buffer);
|
|
903
|
-
|
|
904
|
-
const id = new Binary(idBytes, serverSession.id.id.sub_type);
|
|
905
|
-
|
|
906
|
-
// Manual prototype construction to avoid modifying the constructor of this class
|
|
907
|
-
return Object.setPrototypeOf(
|
|
908
|
-
{
|
|
909
|
-
id: { id },
|
|
910
|
-
lastUse: serverSession.lastUse,
|
|
911
|
-
txnNumber: serverSession.txnNumber,
|
|
912
|
-
isDirty: serverSession.isDirty
|
|
913
|
-
},
|
|
914
|
-
ServerSession.prototype
|
|
915
|
-
);
|
|
916
|
-
}
|
|
917
1003
|
}
|
|
918
1004
|
|
|
919
1005
|
/**
|