@@ -12,7 +12,6 @@ import {
1212 MongoInvalidArgumentError ,
1313 MongoNetworkError ,
1414 MongoNotConnectedError ,
15- MongoOperationTimeoutError ,
1615 MongoRuntimeError ,
1716 MongoServerError ,
1817 MongoTransactionError ,
@@ -29,13 +28,8 @@ import {
2928import type { Topology } from '../sdam/topology' ;
3029import type { ClientSession } from '../sessions' ;
3130import { TimeoutContext } from '../timeout' ;
32- import { RETRY_COST , TOKEN_REFRESH_RATE } from '../token_bucket' ;
33- import {
34- abortable ,
35- ExponentialBackoffProvider ,
36- maxWireVersion ,
37- supportsRetryableWrites
38- } from '../utils' ;
31+ import { RETRY_COST , RETRY_TOKEN_RETURN_RATE } from '../token_bucket' ;
32+ import { abortable , maxWireVersion , supportsRetryableWrites } from '../utils' ;
3933import { AggregateOperation } from './aggregate' ;
4034import { AbstractOperation , Aspect } from './operation' ;
4135import { RunCommandOperation } from './run_command' ;
@@ -194,7 +188,6 @@ type RetryOptions = {
194188 *
195189 * @param operation - The operation to execute
196190 */
197- // Note for Sergey: The rename here could be reverted - I just thought this was more descriptive.
198191async function executeOperationWithRetries <
199192 T extends AbstractOperation ,
200193 TResult = ResultTypeFromOperation < T >
@@ -248,11 +241,6 @@ async function executeOperationWithRetries<
248241 }
249242
250243 const deprioritizedServers = new DeprioritizedServers ( ) ;
251- const backoffDelayProvider = new ExponentialBackoffProvider (
252- 10_000 , // MAX_BACKOFF
253- 100 , // base backoff
254- 2 // backoff rate
255- ) ;
256244
257245 let maxAttempts =
258246 typeof operation . maxAttempts === 'number'
@@ -271,6 +259,7 @@ async function executeOperationWithRetries<
271259 let error : MongoError | null = null ;
272260
273261 for ( let attempt = 0 ; attempt < maxAttempts ; attempt ++ ) {
262+ operation . attemptsMade = attempt + 1 ;
274263 operation . server = server ;
275264
276265 try {
@@ -279,28 +268,20 @@ async function executeOperationWithRetries<
279268 topology . tokenBucket . deposit (
280269 attempt > 0
281270 ? // on successful retry, deposit the retry cost + the refresh rate.
282- TOKEN_REFRESH_RATE + RETRY_COST
271+ RETRY_TOKEN_RETURN_RATE + RETRY_COST
283272 : // otherwise, just deposit the refresh rate.
284- TOKEN_REFRESH_RATE
273+ RETRY_TOKEN_RETURN_RATE
285274 ) ;
286275 return operation . handleOk ( result ) ;
287276 } catch ( error ) {
288277 return operation . handleError ( error ) ;
289278 }
290-
291- // Note for Sergey: This ended up being a larger refactor than I anticipated. But it made more sense to me to put the error handling
292- // that previously lived in the try-block into the catch-block.
293- // The primary motivator for this change was to make error tracking simpler. The post failure but pre-retry logic needs access to both the
294- // most recently encountered operation error _and_ the error we're storing to potentially throw, if we need to (because in some circumstances
295- // the error raised from here is _not_ the most recently encountered operation error). If we chose to keep the previously existing structure,
296- // this would force us to keep track of two errors outside of the for loop. By moving the pre-retry logic into the catch block, it gains
297- // access to `operationError`, and still only requires keeping track of one error outside the loop.
298279 } catch ( operationError ) {
299280 // Should never happen but if it does - propagate the error.
300281 if ( ! ( operationError instanceof MongoError ) ) throw operationError ;
301282
302- if ( ! operationError . hasErrorLabel ( MongoErrorLabel . SystemOverloadedError ) ) {
303- // if an operation fails with an error that does not contain the SystemOverloadError , deposit 1 token.
283+ if ( attempt > 0 && ! operationError . hasErrorLabel ( MongoErrorLabel . SystemOverloadedError ) ) {
284+ // if a retry attempt fails with a non-overload error , deposit 1 token.
304285 topology . tokenBucket . deposit ( RETRY_COST ) ;
305286 }
306287
@@ -327,10 +308,9 @@ async function executeOperationWithRetries<
327308 throw error ;
328309 }
329310
330- maxAttempts =
331- shouldRetry && operationError . hasErrorLabel ( MongoErrorLabel . SystemOverloadedError )
332- ? 6
333- : maxAttempts ;
311+ if ( operationError . hasErrorLabel ( MongoErrorLabel . SystemOverloadedError ) ) {
312+ maxAttempts = Math . min ( 6 , operation . maxAttempts ?? 6 ) ;
313+ }
334314
335315 if ( attempt + 1 >= maxAttempts ) {
336316 throw error ;
@@ -341,17 +321,11 @@ async function executeOperationWithRetries<
341321 throw error ;
342322 }
343323
344- const delayMS = backoffDelayProvider . getNextBackoffDuration ( ) ;
324+ const delayMS = Math . random ( ) * Math . min ( 10_000 , 100 * 2 ** attempt ) ;
345325
346326 // if the delay would exhaust the CSOT timeout, short-circuit.
347327 if ( timeoutContext . csotEnabled ( ) && delayMS > timeoutContext . remainingTimeMS ) {
348- // TODO: is this the right error to throw?
349- throw new MongoOperationTimeoutError (
350- `MongoDB SystemOverload exponential backoff would exceed timeoutMS deadline: remaining CSOT deadline=${ timeoutContext . remainingTimeMS } , backoff delayMS=${ delayMS } ` ,
351- {
352- cause : error
353- }
354- ) ;
328+ throw error ;
355329 }
356330
357331 await setTimeout ( delayMS ) ;
0 commit comments