mongodb 7.0.0-dev.20251211.sha.f88bfe18 → 7.0.0-dev.20251217.sha.c990750f

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.
@@ -400,7 +400,8 @@ export class Server extends TypedEventEmitter<ServerEvents> {
400
400
  error instanceof MongoNetworkError && !(error instanceof MongoNetworkTimeoutError);
401
401
  const isNetworkTimeoutBeforeHandshakeError =
402
402
  error instanceof MongoNetworkError && error.beforeHandshake;
403
- const isAuthHandshakeError = error.hasErrorLabel(MongoErrorLabel.HandshakeError);
403
+ const isAuthOrEstablishmentHandshakeError = error.hasErrorLabel(MongoErrorLabel.HandshakeError);
404
+ const isSystemOverloadError = error.hasErrorLabel(MongoErrorLabel.SystemOverloadedError);
404
405
 
405
406
  // Perhaps questionable and divergent from the spec, but considering MongoParseErrors like state change errors was legacy behavior.
406
407
  if (isStateChangeError(error) || error instanceof MongoParseError) {
@@ -414,7 +415,7 @@ export class Server extends TypedEventEmitter<ServerEvents> {
414
415
  error.addErrorLabel(MongoErrorLabel.ResetPool);
415
416
  }
416
417
  markServerUnknown(this, error);
417
- process.nextTick(() => this.requestCheck());
418
+ queueMicrotask(() => this.requestCheck());
418
419
  return;
419
420
  }
420
421
 
@@ -424,8 +425,12 @@ export class Server extends TypedEventEmitter<ServerEvents> {
424
425
  } else if (
425
426
  isNetworkNonTimeoutError ||
426
427
  isNetworkTimeoutBeforeHandshakeError ||
427
- isAuthHandshakeError
428
+ isAuthOrEstablishmentHandshakeError
428
429
  ) {
430
+ // Do NOT clear the pool if we encounter a system overloaded error.
431
+ if (isSystemOverloadError) {
432
+ return;
433
+ }
429
434
  // from the SDAM spec: The driver MUST synchronize clearing the pool with updating the topology.
430
435
  // In load balanced mode: there is no monitoring, so there is no topology to update. We simply clear the pool.
431
436
  // For other topologies: the `ResetPool` label instructs the topology to clear the server's pool in `updateServer()`.
@@ -1076,7 +1076,7 @@ function processWaitQueue(topology: Topology) {
1076
1076
  if (topology.waitQueue.length > 0) {
1077
1077
  // ensure all server monitors attempt monitoring soon
1078
1078
  for (const [, server] of topology.s.servers) {
1079
- process.nextTick(function scheduleServerCheck() {
1079
+ queueMicrotask(function scheduleServerCheck() {
1080
1080
  return server.requestCheck();
1081
1081
  });
1082
1082
  }
package/src/sessions.ts CHANGED
@@ -726,7 +726,8 @@ export class ClientSession
726
726
  })
727
727
  : null;
728
728
 
729
- const startTime = this.timeoutContext?.csotEnabled()
729
+ // 1. Record the current monotonic time, which will be used to enforce the 120-second timeout before later retry attempts.
730
+ const startTime = this.timeoutContext?.csotEnabled() // This is strictly to appease TS. We must narrow the context to a CSOT context before accessing `.start`.
730
731
  ? this.timeoutContext.start
731
732
  : processTimeMS();
732
733
 
@@ -735,9 +736,13 @@ export class ClientSession
735
736
 
736
737
  try {
737
738
  while (!committed) {
739
+ // 2. Invoke startTransaction on the session
740
+ // 3. If `startTransaction` reported an error, propagate that error to the caller of `withTransaction` and return immediately.
738
741
  this.startTransaction(options); // may throw on error
739
742
 
740
743
  try {
744
+ // 4. Invoke the callback.
745
+ // 5. Control returns to withTransaction. (continued below)
741
746
  const promise = fn(this);
742
747
  if (!isPromiseLike(promise)) {
743
748
  throw new MongoInvalidArgumentError(
@@ -747,16 +752,22 @@ export class ClientSession
747
752
 
748
753
  result = await promise;
749
754
 
755
+ // 5. (cont.) Determine the current state of the ClientSession (continued below)
750
756
  if (
751
757
  this.transaction.state === TxnState.NO_TRANSACTION ||
752
758
  this.transaction.state === TxnState.TRANSACTION_COMMITTED ||
753
759
  this.transaction.state === TxnState.TRANSACTION_ABORTED
754
760
  ) {
755
- // Assume callback intentionally ended the transaction
761
+ // 7. If the ClientSession is in the "no transaction", "transaction aborted", or "transaction committed" state,
762
+ // assume the callback intentionally aborted or committed the transaction and return immediately.
756
763
  return result;
757
764
  }
765
+ // 5. (cont.) and whether the callback reported an error
766
+ // 6. If the callback reported an error:
758
767
  } catch (fnError) {
759
768
  if (!(fnError instanceof MongoError) || fnError instanceof MongoInvalidArgumentError) {
769
+ // This first preemptive abort regardless of TxnState isn't spec,
770
+ // and it's unclear whether it's serving a practical purpose, but this logic is OLD
760
771
  await this.abortTransaction();
761
772
  throw fnError;
762
773
  }
@@ -765,6 +776,8 @@ export class ClientSession
765
776
  this.transaction.state === TxnState.STARTING_TRANSACTION ||
766
777
  this.transaction.state === TxnState.TRANSACTION_IN_PROGRESS
767
778
  ) {
779
+ // 6.i If the ClientSession is in the "starting transaction" or "transaction in progress" state,
780
+ // invoke abortTransaction on the session
768
781
  await this.abortTransaction();
769
782
  }
770
783
 
@@ -772,9 +785,15 @@ export class ClientSession
772
785
  fnError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
773
786
  (this.timeoutContext != null || processTimeMS() - startTime < MAX_TIMEOUT)
774
787
  ) {
788
+ // 6.ii If the callback's error includes a "TransientTransactionError" label and the elapsed time of `withTransaction`
789
+ // is less than 120 seconds, jump back to step two.
775
790
  continue;
776
791
  }
777
792
 
793
+ // 6.iii If the callback's error includes a "UnknownTransactionCommitResult" label, the callback must have manually committed a transaction,
794
+ // propagate the callback's error to the caller of withTransaction and return immediately.
795
+ // The 6.iii check is redundant with 6.iv, so we don't write code for it
796
+ // 6.iv Otherwise, propagate the callback's error to the caller of withTransaction and return immediately.
778
797
  throw fnError;
779
798
  }
780
799
 
@@ -785,8 +804,10 @@ export class ClientSession
785
804
  * apply a majority write concern if commitTransaction is
786
805
  * being retried (see: DRIVERS-601)
787
806
  */
807
+ // 8. Invoke commitTransaction on the session.
788
808
  await this.commitTransaction();
789
809
  committed = true;
810
+ // 9. If commitTransaction reported an error:
790
811
  } catch (commitError) {
791
812
  /*
792
813
  * Note: a maxTimeMS error will have the MaxTimeMSExpired
@@ -800,6 +821,8 @@ export class ClientSession
800
821
  commitError.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult) &&
801
822
  (this.timeoutContext != null || processTimeMS() - startTime < MAX_TIMEOUT)
802
823
  ) {
824
+ // 9.i If the `commitTransaction` error includes a "UnknownTransactionCommitResult" label and the error is not
825
+ // MaxTimeMSExpired and the elapsed time of `withTransaction` is less than 120 seconds, jump back to step eight.
803
826
  continue;
804
827
  }
805
828
 
@@ -807,9 +830,12 @@ export class ClientSession
807
830
  commitError.hasErrorLabel(MongoErrorLabel.TransientTransactionError) &&
808
831
  (this.timeoutContext != null || processTimeMS() - startTime < MAX_TIMEOUT)
809
832
  ) {
833
+ // 9.ii If the commitTransaction error includes a "TransientTransactionError" label
834
+ // and the elapsed time of withTransaction is less than 120 seconds, jump back to step two.
810
835
  break;
811
836
  }
812
837
 
838
+ // 9.iii Otherwise, propagate the commitTransaction error to the caller of withTransaction and return immediately.
813
839
  throw commitError;
814
840
  }
815
841
  }