Skip to content

Instantly share code, notes, and snippets.

@kevboutin
Created January 17, 2025 21:12
Show Gist options
  • Save kevboutin/a53dcbba6d85903c07bd500a6e12bd20 to your computer and use it in GitHub Desktop.
Save kevboutin/a53dcbba6d85903c07bd500a6e12bd20 to your computer and use it in GitHub Desktop.
Provides an exponentially backoff retry mechanism
/**
* Provides a class for managing retry operations.
* @class
*/
module.exports = class ExponentialBackoffRetry {
/**
* Constructor.
*
* @param {Object} [options] The options.
* @param {number} [options.baseDelay] The base delay in milliseconds. Defaults to 1000.
* @param {number} [options.maxDelay] The maximum delay in milliseconds. Defaults to 30000.
* @param {number} [options.maxRetries] The maximum number of retries. Defaults to 5.
* @param {boolean} [options.jitter] When true, prevents a thundering herd issue. Defaults to true.
*/
constructor(options = {}) {
this.baseDelay = options.baseDelay || 1000;
this.maxDelay = options.maxDelay || 30000;
this.maxRetries = options.maxRetries || 5;
this.jitter = options.jitter || true;
}
/**
* Calculate the time delay based on the number of retry attempts.
*
* @param {number} retryCount The number of retries.
* @returns {number} The delay in milliseconds.
*/
calculateDelay(retryCount) {
// Calculate exponential delay: 2^retryCount * baseDelay
let delay = Math.min(this.maxDelay, Math.pow(2, retryCount) * this.baseDelay);
// Add jitter to prevent thundering herd problem.
if (this.jitter) {
delay = delay * (0.5 + Math.random());
}
return delay;
}
/**
* Wait a specified time period.
*
* @param {number} ms Milliseconds.
* @returns {Promise} A Promise to wait in milliseconds given.
*/
wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Execute the function with retries in the event of failure.
*
* @param {Function} fn The function to execute.
* @returns {Promise} A Promise to return after function execution.
*/
async execute(fn) {
let retries = 0;
while (true) {
try {
return await fn();
} catch (error) {
if (retries >= this.maxRetries) {
throw new Error(`Failed after ${retries} retries: ${error.message}`);
}
const delay = this.calculateDelay(retries);
await this.wait(delay);
retries++;
}
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment