mongodb 6.10.0-dev.20241102.sha.2f3fb466 → 6.10.0-dev.20241107.sha.e5582ed7
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/lib/admin.js +3 -2
- package/lib/admin.js.map +1 -1
- package/lib/beta.d.ts +558 -38
- 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/connection.js +78 -6
- package/lib/cmap/connection.js.map +1 -1
- package/lib/cmap/connection_pool.js +14 -9
- 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 +218 -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 +27 -2
- 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/sdam/server.js +4 -1
- package/lib/sdam/server.js.map +1 -1
- package/lib/sdam/server_description.js +2 -0
- package/lib/sdam/server_description.js.map +1 -1
- package/lib/sdam/topology.js +38 -11
- package/lib/sdam/topology.js.map +1 -1
- package/lib/sessions.js +145 -74
- package/lib/sessions.js.map +1 -1
- package/lib/timeout.js +217 -16
- package/lib/timeout.js.map +1 -1
- package/lib/utils.js +31 -17
- package/lib/utils.js.map +1 -1
- package/lib/write_concern.js.map +1 -1
- package/mongodb.d.ts +558 -38
- package/package.json +2 -2
- 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/connection.ts +105 -8
- package/src/cmap/connection_pool.ts +14 -14
- 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 +286 -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 +26 -1
- 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 +152 -45
- package/src/index.ts +26 -4
- package/src/mongo_client.ts +75 -3
- 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/sdam/server.ts +14 -4
- package/src/sdam/server_description.ts +4 -0
- package/src/sdam/topology.ts +43 -18
- package/src/sessions.ts +193 -89
- package/src/timeout.ts +310 -23
- package/src/transactions.ts +1 -1
- package/src/utils.ts +42 -28
- 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;
|
|
@@ -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,8 +287,13 @@ 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) {
|
|
@@ -290,10 +309,6 @@ export class ClientSession
|
|
|
290
309
|
this.hasEnded = true;
|
|
291
310
|
this.emit('ended', this);
|
|
292
311
|
}
|
|
293
|
-
} catch (error) {
|
|
294
|
-
// spec indicates that we should ignore all errors for `endSessions`
|
|
295
|
-
squashError(error);
|
|
296
|
-
} finally {
|
|
297
312
|
maybeClearPinnedConnection(this, { force: true, ...options });
|
|
298
313
|
}
|
|
299
314
|
}
|
|
@@ -446,8 +461,10 @@ export class ClientSession
|
|
|
446
461
|
|
|
447
462
|
/**
|
|
448
463
|
* Commits the currently active transaction in this session.
|
|
464
|
+
*
|
|
465
|
+
* @param options - Optional options, can be used to override `defaultTimeoutMS`.
|
|
449
466
|
*/
|
|
450
|
-
async commitTransaction(): Promise<void> {
|
|
467
|
+
async commitTransaction(options?: { timeoutMS?: number }): Promise<void> {
|
|
451
468
|
if (this.transaction.state === TxnState.NO_TRANSACTION) {
|
|
452
469
|
throw new MongoTransactionError('No transaction started');
|
|
453
470
|
}
|
|
@@ -474,13 +491,31 @@ export class ClientSession
|
|
|
474
491
|
maxTimeMS?: number;
|
|
475
492
|
} = { commitTransaction: 1 };
|
|
476
493
|
|
|
494
|
+
const timeoutMS =
|
|
495
|
+
typeof options?.timeoutMS === 'number'
|
|
496
|
+
? options.timeoutMS
|
|
497
|
+
: typeof this.timeoutMS === 'number'
|
|
498
|
+
? this.timeoutMS
|
|
499
|
+
: null;
|
|
500
|
+
|
|
477
501
|
const wc = this.transaction.options.writeConcern ?? this.clientOptions?.writeConcern;
|
|
478
502
|
if (wc != null) {
|
|
479
|
-
|
|
503
|
+
if (timeoutMS == null && this.timeoutContext == null) {
|
|
504
|
+
WriteConcern.apply(command, { wtimeoutMS: 10000, w: 'majority', ...wc });
|
|
505
|
+
} else {
|
|
506
|
+
const wcKeys = Object.keys(wc);
|
|
507
|
+
if (wcKeys.length > 2 || (!wcKeys.includes('wtimeoutMS') && !wcKeys.includes('wTimeoutMS')))
|
|
508
|
+
// 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
|
|
509
|
+
WriteConcern.apply(command, { ...wc, wtimeoutMS: undefined });
|
|
510
|
+
}
|
|
480
511
|
}
|
|
481
512
|
|
|
482
513
|
if (this.transaction.state === TxnState.TRANSACTION_COMMITTED || this.commitAttempted) {
|
|
483
|
-
|
|
514
|
+
if (timeoutMS == null && this.timeoutContext == null) {
|
|
515
|
+
WriteConcern.apply(command, { wtimeoutMS: 10000, ...wc, w: 'majority' });
|
|
516
|
+
} else {
|
|
517
|
+
WriteConcern.apply(command, { w: 'majority', ...wc, wtimeoutMS: undefined });
|
|
518
|
+
}
|
|
484
519
|
}
|
|
485
520
|
|
|
486
521
|
if (typeof this.transaction.options.maxTimeMS === 'number') {
|
|
@@ -497,8 +532,18 @@ export class ClientSession
|
|
|
497
532
|
bypassPinningCheck: true
|
|
498
533
|
});
|
|
499
534
|
|
|
535
|
+
const timeoutContext =
|
|
536
|
+
this.timeoutContext ??
|
|
537
|
+
(typeof timeoutMS === 'number'
|
|
538
|
+
? TimeoutContext.create({
|
|
539
|
+
serverSelectionTimeoutMS: this.clientOptions.serverSelectionTimeoutMS,
|
|
540
|
+
socketTimeoutMS: this.clientOptions.socketTimeoutMS,
|
|
541
|
+
timeoutMS
|
|
542
|
+
})
|
|
543
|
+
: null);
|
|
544
|
+
|
|
500
545
|
try {
|
|
501
|
-
await executeOperation(this.client, operation);
|
|
546
|
+
await executeOperation(this.client, operation, timeoutContext);
|
|
502
547
|
this.commitAttempted = undefined;
|
|
503
548
|
return;
|
|
504
549
|
} catch (firstCommitError) {
|
|
@@ -516,7 +561,8 @@ export class ClientSession
|
|
|
516
561
|
session: this,
|
|
517
562
|
readPreference: ReadPreference.primary,
|
|
518
563
|
bypassPinningCheck: true
|
|
519
|
-
})
|
|
564
|
+
}),
|
|
565
|
+
timeoutContext
|
|
520
566
|
);
|
|
521
567
|
return;
|
|
522
568
|
} catch (retryCommitError) {
|
|
@@ -549,8 +595,13 @@ export class ClientSession
|
|
|
549
595
|
|
|
550
596
|
/**
|
|
551
597
|
* Aborts the currently active transaction in this session.
|
|
598
|
+
*
|
|
599
|
+
* @param options - Optional options, can be used to override `defaultTimeoutMS`.
|
|
552
600
|
*/
|
|
553
|
-
async abortTransaction(): Promise<void
|
|
601
|
+
async abortTransaction(options?: { timeoutMS?: number }): Promise<void>;
|
|
602
|
+
/** @internal */
|
|
603
|
+
async abortTransaction(options?: { timeoutMS?: number; throwTimeout?: true }): Promise<void>;
|
|
604
|
+
async abortTransaction(options?: { timeoutMS?: number; throwTimeout?: true }): Promise<void> {
|
|
554
605
|
if (this.transaction.state === TxnState.NO_TRANSACTION) {
|
|
555
606
|
throw new MongoTransactionError('No transaction started');
|
|
556
607
|
}
|
|
@@ -580,8 +631,26 @@ export class ClientSession
|
|
|
580
631
|
recoveryToken?: Document;
|
|
581
632
|
} = { abortTransaction: 1 };
|
|
582
633
|
|
|
634
|
+
const timeoutMS =
|
|
635
|
+
typeof options?.timeoutMS === 'number'
|
|
636
|
+
? options.timeoutMS
|
|
637
|
+
: this.timeoutContext?.csotEnabled()
|
|
638
|
+
? this.timeoutContext.timeoutMS // refresh timeoutMS for abort operation
|
|
639
|
+
: typeof this.timeoutMS === 'number'
|
|
640
|
+
? this.timeoutMS
|
|
641
|
+
: null;
|
|
642
|
+
|
|
643
|
+
const timeoutContext =
|
|
644
|
+
timeoutMS != null
|
|
645
|
+
? TimeoutContext.create({
|
|
646
|
+
timeoutMS,
|
|
647
|
+
serverSelectionTimeoutMS: this.clientOptions.serverSelectionTimeoutMS,
|
|
648
|
+
socketTimeoutMS: this.clientOptions.socketTimeoutMS
|
|
649
|
+
})
|
|
650
|
+
: null;
|
|
651
|
+
|
|
583
652
|
const wc = this.transaction.options.writeConcern ?? this.clientOptions?.writeConcern;
|
|
584
|
-
if (wc != null) {
|
|
653
|
+
if (wc != null && timeoutMS == null) {
|
|
585
654
|
WriteConcern.apply(command, { wtimeoutMS: 10000, w: 'majority', ...wc });
|
|
586
655
|
}
|
|
587
656
|
|
|
@@ -596,17 +665,26 @@ export class ClientSession
|
|
|
596
665
|
});
|
|
597
666
|
|
|
598
667
|
try {
|
|
599
|
-
await executeOperation(this.client, operation);
|
|
668
|
+
await executeOperation(this.client, operation, timeoutContext);
|
|
600
669
|
this.unpin();
|
|
601
670
|
return;
|
|
602
671
|
} catch (firstAbortError) {
|
|
603
672
|
this.unpin();
|
|
604
673
|
|
|
674
|
+
if (firstAbortError.name === 'MongoRuntimeError') throw firstAbortError;
|
|
675
|
+
if (options?.throwTimeout && firstAbortError.name === 'MongoOperationTimeoutError') {
|
|
676
|
+
throw firstAbortError;
|
|
677
|
+
}
|
|
678
|
+
|
|
605
679
|
if (firstAbortError instanceof MongoError && isRetryableWriteError(firstAbortError)) {
|
|
606
680
|
try {
|
|
607
|
-
await executeOperation(this.client, operation);
|
|
681
|
+
await executeOperation(this.client, operation, timeoutContext);
|
|
608
682
|
return;
|
|
609
|
-
} catch {
|
|
683
|
+
} catch (secondAbortError) {
|
|
684
|
+
if (secondAbortError.name === 'MongoRuntimeError') throw secondAbortError;
|
|
685
|
+
if (options?.throwTimeout && secondAbortError.name === 'MongoOperationTimeoutError') {
|
|
686
|
+
throw secondAbortError;
|
|
687
|
+
}
|
|
610
688
|
// we do not retry the retry
|
|
611
689
|
}
|
|
612
690
|
}
|
|
@@ -636,6 +714,9 @@ export class ClientSession
|
|
|
636
714
|
* `Promise.allSettled`, `Promise.race`, etc to parallelize operations inside a transaction is
|
|
637
715
|
* undefined behaviour.
|
|
638
716
|
*
|
|
717
|
+
* **IMPORTANT:** When running an operation inside a `withTransaction` callback, if it is not
|
|
718
|
+
* provided the explicit session in its options, it will not be part of the transaction and it will not respect timeoutMS.
|
|
719
|
+
*
|
|
639
720
|
*
|
|
640
721
|
* @remarks
|
|
641
722
|
* - 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 +742,119 @@ export class ClientSession
|
|
|
661
742
|
*/
|
|
662
743
|
async withTransaction<T = any>(
|
|
663
744
|
fn: WithTransactionCallback<T>,
|
|
664
|
-
options?: TransactionOptions
|
|
745
|
+
options?: TransactionOptions & {
|
|
746
|
+
/**
|
|
747
|
+
* Configures a timeoutMS expiry for the entire withTransactionCallback.
|
|
748
|
+
*
|
|
749
|
+
* @remarks
|
|
750
|
+
* - The remaining timeout will not be applied to callback operations that do not use the ClientSession.
|
|
751
|
+
* - Overriding timeoutMS for operations executed using the explicit session inside the provided callback will result in a client-side error.
|
|
752
|
+
*/
|
|
753
|
+
timeoutMS?: number;
|
|
754
|
+
}
|
|
665
755
|
): Promise<T> {
|
|
666
756
|
const MAX_TIMEOUT = 120000;
|
|
667
|
-
const startTime = now();
|
|
668
757
|
|
|
669
|
-
|
|
670
|
-
|
|
758
|
+
const timeoutMS = options?.timeoutMS ?? this.timeoutMS ?? null;
|
|
759
|
+
this.timeoutContext =
|
|
760
|
+
timeoutMS != null
|
|
761
|
+
? TimeoutContext.create({
|
|
762
|
+
timeoutMS,
|
|
763
|
+
serverSelectionTimeoutMS: this.clientOptions.serverSelectionTimeoutMS,
|
|
764
|
+
socketTimeoutMS: this.clientOptions.socketTimeoutMS
|
|
765
|
+
})
|
|
766
|
+
: null;
|
|
671
767
|
|
|
672
|
-
|
|
673
|
-
this.startTransaction(options); // may throw on error
|
|
768
|
+
const startTime = this.timeoutContext?.csotEnabled() ? this.timeoutContext.start : now();
|
|
674
769
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (!isPromiseLike(promise)) {
|
|
678
|
-
throw new MongoInvalidArgumentError(
|
|
679
|
-
'Function provided to `withTransaction` must return a Promise'
|
|
680
|
-
);
|
|
681
|
-
}
|
|
770
|
+
let committed = false;
|
|
771
|
+
let result: any;
|
|
682
772
|
|
|
683
|
-
|
|
773
|
+
try {
|
|
774
|
+
while (!committed) {
|
|
775
|
+
this.startTransaction(options); // may throw on error
|
|
684
776
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
}
|
|
693
|
-
} catch (fnError) {
|
|
694
|
-
if (!(fnError instanceof MongoError) || fnError instanceof MongoInvalidArgumentError) {
|
|
695
|
-
await this.abortTransaction();
|
|
696
|
-
throw fnError;
|
|
697
|
-
}
|
|
777
|
+
try {
|
|
778
|
+
const promise = fn(this);
|
|
779
|
+
if (!isPromiseLike(promise)) {
|
|
780
|
+
throw new MongoInvalidArgumentError(
|
|
781
|
+
'Function provided to `withTransaction` must return a Promise'
|
|
782
|
+
);
|
|
783
|
+
}
|
|
698
784
|
|
|
699
|
-
|
|
700
|
-
this.transaction.state === TxnState.STARTING_TRANSACTION ||
|
|
701
|
-
this.transaction.state === TxnState.TRANSACTION_IN_PROGRESS
|
|
702
|
-
) {
|
|
703
|
-
await this.abortTransaction();
|
|
704
|
-
}
|
|
785
|
+
result = await promise;
|
|
705
786
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
787
|
+
if (
|
|
788
|
+
this.transaction.state === TxnState.NO_TRANSACTION ||
|
|
789
|
+
this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
|
|
790
|
+
this.transaction.state === TxnState.TRANSACTION_ABORTED
|
|
791
|
+
) {
|
|
792
|
+
// Assume callback intentionally ended the transaction
|
|
793
|
+
return result;
|
|
794
|
+
}
|
|
795
|
+
} catch (fnError) {
|
|
796
|
+
if (!(fnError instanceof MongoError) || fnError instanceof MongoInvalidArgumentError) {
|
|
797
|
+
await this.abortTransaction();
|
|
798
|
+
throw fnError;
|
|
799
|
+
}
|
|
715
800
|
|
|
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
801
|
if (
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
now() - startTime < MAX_TIMEOUT
|
|
802
|
+
this.transaction.state === TxnState.STARTING_TRANSACTION ||
|
|
803
|
+
this.transaction.state === TxnState.TRANSACTION_IN_PROGRESS
|
|
737
804
|
) {
|
|
738
|
-
|
|
805
|
+
await this.abortTransaction();
|
|
739
806
|
}
|
|
740
807
|
|
|
741
808
|
if (
|
|
742
|
-
|
|
743
|
-
now() - startTime < MAX_TIMEOUT
|
|
809
|
+
fnError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
|
|
810
|
+
(this.timeoutContext != null || now() - startTime < MAX_TIMEOUT)
|
|
744
811
|
) {
|
|
745
|
-
|
|
812
|
+
continue;
|
|
746
813
|
}
|
|
747
814
|
|
|
748
|
-
throw
|
|
815
|
+
throw fnError;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
while (!committed) {
|
|
819
|
+
try {
|
|
820
|
+
/*
|
|
821
|
+
* We will rely on ClientSession.commitTransaction() to
|
|
822
|
+
* apply a majority write concern if commitTransaction is
|
|
823
|
+
* being retried (see: DRIVERS-601)
|
|
824
|
+
*/
|
|
825
|
+
await this.commitTransaction();
|
|
826
|
+
committed = true;
|
|
827
|
+
} catch (commitError) {
|
|
828
|
+
/*
|
|
829
|
+
* Note: a maxTimeMS error will have the MaxTimeMSExpired
|
|
830
|
+
* code (50) and can be reported as a top-level error or
|
|
831
|
+
* inside writeConcernError, ex.
|
|
832
|
+
* { ok:0, code: 50, codeName: 'MaxTimeMSExpired' }
|
|
833
|
+
* { ok:1, writeConcernError: { code: 50, codeName: 'MaxTimeMSExpired' } }
|
|
834
|
+
*/
|
|
835
|
+
if (
|
|
836
|
+
!isMaxTimeMSExpiredError(commitError) &&
|
|
837
|
+
commitError.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult) &&
|
|
838
|
+
(this.timeoutContext != null || now() - startTime < MAX_TIMEOUT)
|
|
839
|
+
) {
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
if (
|
|
844
|
+
commitError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
|
|
845
|
+
(this.timeoutContext != null || now() - startTime < MAX_TIMEOUT)
|
|
846
|
+
) {
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
throw commitError;
|
|
851
|
+
}
|
|
749
852
|
}
|
|
750
853
|
}
|
|
854
|
+
return result;
|
|
855
|
+
} finally {
|
|
856
|
+
this.timeoutContext = null;
|
|
751
857
|
}
|
|
752
|
-
|
|
753
|
-
return result;
|
|
754
858
|
}
|
|
755
859
|
}
|
|
756
860
|
|