Move calculateDelay back to private. Move abort signal check
This commit is contained in:
@@ -135,33 +135,6 @@ export class ExponentialBackoff {
|
|||||||
return backoff.run(taskFn, onError);
|
return backoff.run(taskFn, onError);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the delay before we should attempt to retry
|
|
||||||
*
|
|
||||||
* @param options - The configuration for the exponential backoff
|
|
||||||
* @param attempt - The current attempt number
|
|
||||||
* @returns The time in milliseconds before another attempt should be made
|
|
||||||
*/
|
|
||||||
public static calculateDelay(options: ExponentialBackoffOptions, attempt: number): number {
|
|
||||||
// Get the power of the growth rate
|
|
||||||
const power = options.growthRate ** attempt;
|
|
||||||
|
|
||||||
// Get the delay before jitter or limit
|
|
||||||
const rawDelay = options.baseDelay * power;
|
|
||||||
|
|
||||||
// Cap the delay to the maximum. Do this before the jitter so jitter does not become larger than delay
|
|
||||||
const cappedDelay = Math.min(rawDelay, options.maxDelay);
|
|
||||||
|
|
||||||
// Get a random number for the amount to "jitter" the delay by
|
|
||||||
const jitterAmount = Math.random();
|
|
||||||
|
|
||||||
// Calculate the jitter
|
|
||||||
const jitter = jitterAmount * options.jitter * cappedDelay;
|
|
||||||
|
|
||||||
// Subtract the jitter from the delay
|
|
||||||
return cappedDelay - jitter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the options for the exponential backoff
|
* Validate the options for the exponential backoff
|
||||||
*
|
*
|
||||||
@@ -273,6 +246,12 @@ export class ExponentialBackoff {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the abort signal has been activated
|
||||||
|
if (abortController.signal.aborted) {
|
||||||
|
// Throw an error if the abort signal has been activated
|
||||||
|
throw new ExponentialBackoffStoppedRetriesError(abortController.signal.reason);
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate the count for next attempt. Do this now so we can exit before waiting and before running the next attempt.
|
// Calculate the count for next attempt. Do this now so we can exit before waiting and before running the next attempt.
|
||||||
const nextAttemptCount = attempt + 1;
|
const nextAttemptCount = attempt + 1;
|
||||||
const nextAttemptExceedsMaxAttempts = nextAttemptCount >= this.#options.maxAttempts;
|
const nextAttemptExceedsMaxAttempts = nextAttemptCount >= this.#options.maxAttempts;
|
||||||
@@ -282,14 +261,8 @@ export class ExponentialBackoff {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the abort signal has been activated
|
|
||||||
if (abortController.signal.aborted) {
|
|
||||||
// Throw an error if the abort signal has been activated
|
|
||||||
throw new ExponentialBackoffStoppedRetriesError(abortController.signal.reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait before going to the next attempt
|
// Wait before going to the next attempt
|
||||||
const delay = ExponentialBackoff.calculateDelay(this.#options, attempt);
|
const delay = this.#calculateDelay(this.#options, attempt);
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
|
|
||||||
attempt++;
|
attempt++;
|
||||||
@@ -298,4 +271,31 @@ export class ExponentialBackoff {
|
|||||||
// We completed the loop without ever succeeding. Throw an ExponentialBackoffMaxRetriesHitError with all the errors we got
|
// We completed the loop without ever succeeding. Throw an ExponentialBackoffMaxRetriesHitError with all the errors we got
|
||||||
throw new ExponentialBackoffMaxRetriesHitError(errors);
|
throw new ExponentialBackoffMaxRetriesHitError(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the delay before we should attempt to retry
|
||||||
|
*
|
||||||
|
* @param options - The configuration for the exponential backoff
|
||||||
|
* @param attempt - The current attempt number
|
||||||
|
* @returns The time in milliseconds before another attempt should be made
|
||||||
|
*/
|
||||||
|
#calculateDelay(options: ExponentialBackoffOptions, attempt: number): number {
|
||||||
|
// Get the power of the growth rate
|
||||||
|
const power = options.growthRate ** attempt;
|
||||||
|
|
||||||
|
// Get the delay before jitter or limit
|
||||||
|
const rawDelay = options.baseDelay * power;
|
||||||
|
|
||||||
|
// Cap the delay to the maximum. Do this before the jitter so jitter does not become larger than delay
|
||||||
|
const cappedDelay = Math.min(rawDelay, options.maxDelay);
|
||||||
|
|
||||||
|
// Get a random number for the amount to "jitter" the delay by
|
||||||
|
const jitterAmount = Math.random();
|
||||||
|
|
||||||
|
// Calculate the jitter
|
||||||
|
const jitter = jitterAmount * options.jitter * cappedDelay;
|
||||||
|
|
||||||
|
// Subtract the jitter from the delay
|
||||||
|
return cappedDelay - jitter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -586,27 +586,6 @@ const testExponentialBackoffValidateOptionsRejectsNaN = (): void => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Tests that calculateDelay will not result in NaN from extremely large growth rates and attempts */
|
|
||||||
const testExponentialBackoffCalculateDelayDoesNotResultInNaN = (): void => {
|
|
||||||
// Large number, 1 trillion.
|
|
||||||
// Theory being that 1 trillion to the power of 1 trillion should be a very large number and cause either an unsafe value or a NaN.
|
|
||||||
const largeNumber = 1_000_000_000_000;
|
|
||||||
|
|
||||||
// Test the calculateDelay function
|
|
||||||
const result = ExponentialBackoff.calculateDelay(
|
|
||||||
{
|
|
||||||
baseDelay: 10000,
|
|
||||||
growthRate: largeNumber,
|
|
||||||
jitter: 0,
|
|
||||||
maxDelay: 10_000,
|
|
||||||
maxAttempts: largeNumber,
|
|
||||||
},
|
|
||||||
largeNumber,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test to ensure it was bounded to the max delay
|
|
||||||
expect(result).toBe(10_000);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Tests that passing undefined into the constructor does not cause an error during spread */
|
/** Tests that passing undefined into the constructor does not cause an error during spread */
|
||||||
const testExponentialBackoffConstructorDoesNotCauseErrorDuringSpread = async (): Promise<void> => {
|
const testExponentialBackoffConstructorDoesNotCauseErrorDuringSpread = async (): Promise<void> => {
|
||||||
@@ -649,10 +628,6 @@ const runTests = async (): Promise<void> => {
|
|||||||
test('ExponentialBackoff.validateOptions: rejects Infinity', testExponentialBackoffValidateOptionsRejectsNonFiniteValues);
|
test('ExponentialBackoff.validateOptions: rejects Infinity', testExponentialBackoffValidateOptionsRejectsNonFiniteValues);
|
||||||
test('ExponentialBackoff.validateOptions: rejects non-integer values', testExponentialBackoffValidateOptionsRejectsNonIntegerValues);
|
test('ExponentialBackoff.validateOptions: rejects non-integer values', testExponentialBackoffValidateOptionsRejectsNonIntegerValues);
|
||||||
test('ExponentialBackoff.validateOptions: rejects NaN', testExponentialBackoffValidateOptionsRejectsNaN);
|
test('ExponentialBackoff.validateOptions: rejects NaN', testExponentialBackoffValidateOptionsRejectsNaN);
|
||||||
test(
|
|
||||||
'ExponentialBackoff: calculateDelay does not result in NaN from extremely large growth rates and attempts',
|
|
||||||
testExponentialBackoffCalculateDelayDoesNotResultInNaN,
|
|
||||||
);
|
|
||||||
test('ExponentialBackoff: constructor does not cause an error during spread', testExponentialBackoffConstructorDoesNotCauseErrorDuringSpread);
|
test('ExponentialBackoff: constructor does not cause an error during spread', testExponentialBackoffConstructorDoesNotCauseErrorDuringSpread);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user