mongodb 6.10.0-dev.20241106.sha.dc3fe957 → 6.10.0-dev.20241108.sha.fd7acde6

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.
Files changed (215) hide show
  1. package/lib/admin.js +3 -2
  2. package/lib/admin.js.map +1 -1
  3. package/lib/beta.d.ts +558 -40
  4. package/lib/bulk/common.js +4 -4
  5. package/lib/bulk/common.js.map +1 -1
  6. package/lib/change_stream.js +111 -51
  7. package/lib/change_stream.js.map +1 -1
  8. package/lib/client-side-encryption/auto_encrypter.js +8 -5
  9. package/lib/client-side-encryption/auto_encrypter.js.map +1 -1
  10. package/lib/client-side-encryption/client_encryption.js +48 -18
  11. package/lib/client-side-encryption/client_encryption.js.map +1 -1
  12. package/lib/client-side-encryption/state_machine.js +43 -29
  13. package/lib/client-side-encryption/state_machine.js.map +1 -1
  14. package/lib/cmap/auth/mongo_credentials.js +2 -1
  15. package/lib/cmap/auth/mongo_credentials.js.map +1 -1
  16. package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js +38 -0
  17. package/lib/cmap/auth/mongodb_oidc/k8s_machine_workflow.js.map +1 -0
  18. package/lib/cmap/auth/mongodb_oidc.js +2 -0
  19. package/lib/cmap/auth/mongodb_oidc.js.map +1 -1
  20. package/lib/cmap/connection.js +78 -6
  21. package/lib/cmap/connection.js.map +1 -1
  22. package/lib/cmap/connection_pool.js +14 -9
  23. package/lib/cmap/connection_pool.js.map +1 -1
  24. package/lib/cmap/wire_protocol/on_data.js +5 -1
  25. package/lib/cmap/wire_protocol/on_data.js.map +1 -1
  26. package/lib/cmap/wire_protocol/responses.js +30 -0
  27. package/lib/cmap/wire_protocol/responses.js.map +1 -1
  28. package/lib/collection.js +62 -3
  29. package/lib/collection.js.map +1 -1
  30. package/lib/connection_string.js +2 -0
  31. package/lib/connection_string.js.map +1 -1
  32. package/lib/cursor/abstract_cursor.js +218 -38
  33. package/lib/cursor/abstract_cursor.js.map +1 -1
  34. package/lib/cursor/aggregation_cursor.js +29 -7
  35. package/lib/cursor/aggregation_cursor.js.map +1 -1
  36. package/lib/cursor/change_stream_cursor.js +2 -2
  37. package/lib/cursor/change_stream_cursor.js.map +1 -1
  38. package/lib/cursor/client_bulk_write_cursor.js +1 -1
  39. package/lib/cursor/client_bulk_write_cursor.js.map +1 -1
  40. package/lib/cursor/find_cursor.js +18 -8
  41. package/lib/cursor/find_cursor.js.map +1 -1
  42. package/lib/cursor/list_collections_cursor.js +1 -1
  43. package/lib/cursor/list_collections_cursor.js.map +1 -1
  44. package/lib/cursor/list_indexes_cursor.js +1 -1
  45. package/lib/cursor/list_indexes_cursor.js.map +1 -1
  46. package/lib/cursor/run_command_cursor.js +6 -4
  47. package/lib/cursor/run_command_cursor.js.map +1 -1
  48. package/lib/db.js +63 -3
  49. package/lib/db.js.map +1 -1
  50. package/lib/error.js +27 -2
  51. package/lib/error.js.map +1 -1
  52. package/lib/explain.js +57 -1
  53. package/lib/explain.js.map +1 -1
  54. package/lib/gridfs/download.js +31 -3
  55. package/lib/gridfs/download.js.map +1 -1
  56. package/lib/gridfs/index.js +49 -14
  57. package/lib/gridfs/index.js.map +1 -1
  58. package/lib/gridfs/upload.js +80 -22
  59. package/lib/gridfs/upload.js.map +1 -1
  60. package/lib/index.js +9 -5
  61. package/lib/index.js.map +1 -1
  62. package/lib/mongo_client.js +70 -1
  63. package/lib/mongo_client.js.map +1 -1
  64. package/lib/operations/aggregate.js +2 -2
  65. package/lib/operations/aggregate.js.map +1 -1
  66. package/lib/operations/bulk_write.js +7 -2
  67. package/lib/operations/bulk_write.js.map +1 -1
  68. package/lib/operations/client_bulk_write/client_bulk_write.js +3 -3
  69. package/lib/operations/client_bulk_write/client_bulk_write.js.map +1 -1
  70. package/lib/operations/client_bulk_write/executor.js +14 -3
  71. package/lib/operations/client_bulk_write/executor.js.map +1 -1
  72. package/lib/operations/command.js +5 -2
  73. package/lib/operations/command.js.map +1 -1
  74. package/lib/operations/count.js +2 -2
  75. package/lib/operations/count.js.map +1 -1
  76. package/lib/operations/create_collection.js +8 -7
  77. package/lib/operations/create_collection.js.map +1 -1
  78. package/lib/operations/delete.js +6 -6
  79. package/lib/operations/delete.js.map +1 -1
  80. package/lib/operations/distinct.js +2 -2
  81. package/lib/operations/distinct.js.map +1 -1
  82. package/lib/operations/drop.js +8 -8
  83. package/lib/operations/drop.js.map +1 -1
  84. package/lib/operations/estimated_document_count.js +2 -2
  85. package/lib/operations/estimated_document_count.js.map +1 -1
  86. package/lib/operations/execute_operation.js +16 -10
  87. package/lib/operations/execute_operation.js.map +1 -1
  88. package/lib/operations/find.js +6 -3
  89. package/lib/operations/find.js.map +1 -1
  90. package/lib/operations/find_and_modify.js +2 -2
  91. package/lib/operations/find_and_modify.js.map +1 -1
  92. package/lib/operations/get_more.js +2 -1
  93. package/lib/operations/get_more.js.map +1 -1
  94. package/lib/operations/indexes.js +6 -6
  95. package/lib/operations/indexes.js.map +1 -1
  96. package/lib/operations/insert.js +6 -6
  97. package/lib/operations/insert.js.map +1 -1
  98. package/lib/operations/kill_cursors.js +5 -2
  99. package/lib/operations/kill_cursors.js.map +1 -1
  100. package/lib/operations/list_collections.js +2 -2
  101. package/lib/operations/list_collections.js.map +1 -1
  102. package/lib/operations/list_databases.js +2 -2
  103. package/lib/operations/list_databases.js.map +1 -1
  104. package/lib/operations/operation.js.map +1 -1
  105. package/lib/operations/profiling_level.js +2 -2
  106. package/lib/operations/profiling_level.js.map +1 -1
  107. package/lib/operations/remove_user.js +2 -2
  108. package/lib/operations/remove_user.js.map +1 -1
  109. package/lib/operations/rename.js +2 -2
  110. package/lib/operations/rename.js.map +1 -1
  111. package/lib/operations/run_command.js +6 -4
  112. package/lib/operations/run_command.js.map +1 -1
  113. package/lib/operations/search_indexes/create.js +5 -2
  114. package/lib/operations/search_indexes/create.js.map +1 -1
  115. package/lib/operations/search_indexes/drop.js +2 -2
  116. package/lib/operations/search_indexes/drop.js.map +1 -1
  117. package/lib/operations/search_indexes/update.js +2 -2
  118. package/lib/operations/search_indexes/update.js.map +1 -1
  119. package/lib/operations/set_profiling_level.js +2 -2
  120. package/lib/operations/set_profiling_level.js.map +1 -1
  121. package/lib/operations/stats.js +2 -2
  122. package/lib/operations/stats.js.map +1 -1
  123. package/lib/operations/update.js +8 -8
  124. package/lib/operations/update.js.map +1 -1
  125. package/lib/operations/validate_collection.js +2 -2
  126. package/lib/operations/validate_collection.js.map +1 -1
  127. package/lib/sdam/common.js +0 -7
  128. package/lib/sdam/common.js.map +1 -1
  129. package/lib/sdam/server.js +4 -1
  130. package/lib/sdam/server.js.map +1 -1
  131. package/lib/sdam/server_description.js +2 -0
  132. package/lib/sdam/server_description.js.map +1 -1
  133. package/lib/sdam/topology.js +38 -15
  134. package/lib/sdam/topology.js.map +1 -1
  135. package/lib/sessions.js +145 -74
  136. package/lib/sessions.js.map +1 -1
  137. package/lib/timeout.js +217 -16
  138. package/lib/timeout.js.map +1 -1
  139. package/lib/utils.js +31 -17
  140. package/lib/utils.js.map +1 -1
  141. package/lib/write_concern.js.map +1 -1
  142. package/mongodb.d.ts +558 -40
  143. package/package.json +3 -2
  144. package/src/admin.ts +6 -2
  145. package/src/bulk/common.ts +17 -5
  146. package/src/change_stream.ts +127 -52
  147. package/src/client-side-encryption/auto_encrypter.ts +12 -5
  148. package/src/client-side-encryption/client_encryption.ts +103 -20
  149. package/src/client-side-encryption/state_machine.ts +66 -32
  150. package/src/cmap/auth/mongo_credentials.ts +3 -2
  151. package/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts +38 -0
  152. package/src/cmap/auth/mongodb_oidc.ts +3 -1
  153. package/src/cmap/connection.ts +105 -8
  154. package/src/cmap/connection_pool.ts +14 -14
  155. package/src/cmap/wire_protocol/on_data.ts +11 -1
  156. package/src/cmap/wire_protocol/responses.ts +35 -1
  157. package/src/collection.ts +81 -9
  158. package/src/connection_string.ts +2 -0
  159. package/src/cursor/abstract_cursor.ts +286 -39
  160. package/src/cursor/aggregation_cursor.ts +54 -8
  161. package/src/cursor/change_stream_cursor.ts +6 -2
  162. package/src/cursor/client_bulk_write_cursor.ts +6 -2
  163. package/src/cursor/find_cursor.ts +40 -9
  164. package/src/cursor/list_collections_cursor.ts +1 -1
  165. package/src/cursor/list_indexes_cursor.ts +1 -1
  166. package/src/cursor/run_command_cursor.ts +50 -5
  167. package/src/db.ts +75 -7
  168. package/src/error.ts +26 -1
  169. package/src/explain.ts +85 -0
  170. package/src/gridfs/download.ts +43 -4
  171. package/src/gridfs/index.ts +64 -16
  172. package/src/gridfs/upload.ts +152 -45
  173. package/src/index.ts +27 -5
  174. package/src/mongo_client.ts +75 -3
  175. package/src/operations/aggregate.ts +10 -2
  176. package/src/operations/bulk_write.ts +9 -2
  177. package/src/operations/client_bulk_write/client_bulk_write.ts +11 -3
  178. package/src/operations/client_bulk_write/executor.ts +15 -3
  179. package/src/operations/command.ts +18 -8
  180. package/src/operations/count.ts +10 -3
  181. package/src/operations/create_collection.ts +14 -7
  182. package/src/operations/delete.ts +15 -6
  183. package/src/operations/distinct.ts +7 -2
  184. package/src/operations/drop.ts +18 -8
  185. package/src/operations/estimated_document_count.ts +7 -2
  186. package/src/operations/execute_operation.ts +22 -13
  187. package/src/operations/find.ts +17 -5
  188. package/src/operations/find_and_modify.ts +7 -2
  189. package/src/operations/get_more.ts +4 -1
  190. package/src/operations/indexes.ts +20 -7
  191. package/src/operations/insert.ts +13 -6
  192. package/src/operations/kill_cursors.ts +10 -2
  193. package/src/operations/list_collections.ts +10 -1
  194. package/src/operations/list_databases.ts +9 -2
  195. package/src/operations/operation.ts +16 -2
  196. package/src/operations/profiling_level.ts +7 -2
  197. package/src/operations/remove_user.ts +7 -2
  198. package/src/operations/rename.ts +7 -2
  199. package/src/operations/run_command.ts +23 -4
  200. package/src/operations/search_indexes/create.ts +10 -2
  201. package/src/operations/search_indexes/drop.ts +7 -2
  202. package/src/operations/search_indexes/update.ts +7 -2
  203. package/src/operations/set_profiling_level.ts +4 -2
  204. package/src/operations/stats.ts +7 -2
  205. package/src/operations/update.ts +16 -8
  206. package/src/operations/validate_collection.ts +7 -2
  207. package/src/sdam/common.ts +0 -11
  208. package/src/sdam/server.ts +14 -4
  209. package/src/sdam/server_description.ts +4 -0
  210. package/src/sdam/topology.ts +43 -27
  211. package/src/sessions.ts +193 -89
  212. package/src/timeout.ts +310 -23
  213. package/src/transactions.ts +1 -1
  214. package/src/utils.ts +42 -28
  215. 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
- /** @internal
62
- * The value of timeoutMS used for CSOT. Used to override client timeoutMS */
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?: MongoOptions;
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
- /** @internal */
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?: MongoOptions
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
- WriteConcern.apply(command, { wtimeoutMS: 10000, w: 'majority', ...wc });
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
- WriteConcern.apply(command, { wtimeoutMS: 10000, ...wc, w: 'majority' });
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
- let committed = false;
670
- let result: any;
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
- while (!committed) {
673
- this.startTransaction(options); // may throw on error
768
+ const startTime = this.timeoutContext?.csotEnabled() ? this.timeoutContext.start : now();
674
769
 
675
- try {
676
- const promise = fn(this);
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
- result = await promise;
773
+ try {
774
+ while (!committed) {
775
+ this.startTransaction(options); // may throw on error
684
776
 
685
- if (
686
- this.transaction.state === TxnState.NO_TRANSACTION ||
687
- this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
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
- }
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
- if (
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
- if (
707
- fnError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
708
- now() - startTime < MAX_TIMEOUT
709
- ) {
710
- continue;
711
- }
712
-
713
- throw fnError;
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
- !isMaxTimeMSExpiredError(commitError) &&
735
- commitError.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult) &&
736
- now() - startTime < MAX_TIMEOUT
802
+ this.transaction.state === TxnState.STARTING_TRANSACTION ||
803
+ this.transaction.state === TxnState.TRANSACTION_IN_PROGRESS
737
804
  ) {
738
- continue;
805
+ await this.abortTransaction();
739
806
  }
740
807
 
741
808
  if (
742
- commitError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
743
- now() - startTime < MAX_TIMEOUT
809
+ fnError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
810
+ (this.timeoutContext != null || now() - startTime < MAX_TIMEOUT)
744
811
  ) {
745
- break;
812
+ continue;
746
813
  }
747
814
 
748
- throw commitError;
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