Skip to content

Commit 605136e

Browse files
add exponential backoff to withTransaction retry loop
1 parent d26230d commit 605136e

File tree

1 file changed

+31
-2
lines changed

1 file changed

+31
-2
lines changed

src/sessions.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { setTimeout } from 'timers/promises';
2+
13
import { Binary, type Document, Long, type Timestamp } from './bson';
24
import type { CommandOptions, Connection } from './cmap/connection';
35
import { ConnectionPoolMetrics } from './cmap/metrics';
@@ -776,7 +778,7 @@ export class ClientSession
776778
throw fnError;
777779
}
778780

779-
while (!committed) {
781+
for (let retry = 0; !committed; ++retry) {
780782
try {
781783
/*
782784
* We will rely on ClientSession.commitTransaction() to
@@ -786,9 +788,23 @@ export class ClientSession
786788
await this.commitTransaction();
787789
committed = true;
788790
} catch (commitError) {
791+
const hasNotTimedOut =
792+
this.timeoutContext?.csotEnabled() || now() - startTime < MAX_TIMEOUT;
793+
794+
/**
795+
* will the provided backoffMS exceed the withTransaction's deadline?
796+
*/
797+
const willExceedTransactionDeadline = (backoffMS: number) => {
798+
return (
799+
(this.timeoutContext?.csotEnabled() &&
800+
backoffMS > this.timeoutContext.remainingTimeMS) ||
801+
now() + backoffMS > startTime + MAX_TIMEOUT
802+
);
803+
};
804+
789805
// If CSOT is enabled, we repeatedly retry until timeoutMS expires.
790806
// If CSOT is not enabled, do we still have time remaining or have we timed out?
791-
if (this.timeoutContext?.csotEnabled() || now() - startTime < MAX_TIMEOUT) {
807+
if (hasNotTimedOut) {
792808
if (
793809
!isMaxTimeMSExpiredError(commitError) &&
794810
commitError.hasErrorLabel(MongoErrorLabel.UnknownTransactionCommitResult)
@@ -800,6 +816,19 @@ export class ClientSession
800816
* { ok:0, code: 50, codeName: 'MaxTimeMSExpired' }
801817
* { ok:1, writeConcernError: { code: 50, codeName: 'MaxTimeMSExpired' } }
802818
*/
819+
820+
const BACKOFF_INITIAL_MS = 1;
821+
const BACKOFF_MAX_MS = 500;
822+
const jitter = Math.random();
823+
const backoffMS =
824+
jitter * Math.min(BACKOFF_INITIAL_MS * 1.25 ** retry, BACKOFF_MAX_MS);
825+
826+
if (willExceedTransactionDeadline(backoffMS)) {
827+
break;
828+
}
829+
830+
await setTimeout(backoffMS);
831+
803832
continue;
804833
}
805834

0 commit comments

Comments
 (0)