or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-retry

Abstraction for exponential and custom retry strategies for failed operations.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/retry@0.13.x

To install, run

npx @tessl/cli install tessl/npm-retry@0.13.0

index.mddocs/

Retry

Retry provides abstraction for exponential and custom retry strategies for failed operations. It offers both operation-based retry patterns through RetryOperation objects and functional wrapper patterns that can automatically add retry logic to existing functions.

Package Information

  • Package Name: retry
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install retry
  • Engine Requirements: Node.js >= 4

Core Imports

const retry = require("retry");

Note: This package uses CommonJS exports only. ES module syntax can be used with bundlers or transpilers:

import retry from "retry";

Individual function imports:

const { operation, timeouts, createTimeout, wrap } = require("retry");

Basic Usage

const retry = require("retry");

function faultTolerantResolve(address, cb) {
  const operation = retry.operation();

  operation.attempt(function(currentAttempt) {
    dns.resolve(address, function(err, addresses) {
      if (operation.retry(err)) {
        return;
      }

      cb(err ? operation.mainError() : null, addresses);
    });
  });
}

Architecture

Retry is built around several key components:

  • RetryOperation Class: Manages individual retry operation lifecycle with error collection and timer management
  • Exponential Backoff: Configurable timeout calculation using exponential growth with optional randomization
  • Error Aggregation: Collects and analyzes errors across retry attempts to identify most common failures
  • Method Wrapping: Automatic retry logic injection into existing object methods
  • Timer Management: Precise timeout control with optional unreferencing for process exit handling

Capabilities

Retry Operations

Create and manage retry operations with exponential backoff strategies. The core functionality for handling failed operations with configurable retry policies.

/**
 * Creates a new RetryOperation instance with computed timeouts
 * @param {Object} [options] - Configuration options for retry behavior
 * @param {number} [options.retries=10] - Maximum number of retry attempts
 * @param {number} [options.factor=2] - Exponential backoff factor
 * @param {number} [options.minTimeout=1000] - Minimum timeout in milliseconds
 * @param {number} [options.maxTimeout=Infinity] - Maximum timeout in milliseconds
 * @param {boolean} [options.randomize=false] - Add randomization to timeouts
 * @param {boolean} [options.forever=false] - Whether to retry forever
 * @param {boolean} [options.unref=false] - Whether to unref setTimeout calls
 * @param {number} [options.maxRetryTime=Infinity] - Maximum time allowed for retries in milliseconds
 * @returns {RetryOperation} RetryOperation instance
 */
function operation(options);

Usage Examples:

const retry = require("retry");

// Basic retry with default settings (10 retries, exponential backoff)
const operation = retry.operation();

// Custom retry configuration
const operation = retry.operation({
  retries: 5,
  factor: 3,
  minTimeout: 1 * 1000,
  maxTimeout: 60 * 1000,
  randomize: true,
});

// Retry forever with maximum retry time limit
const operation = retry.operation({
  forever: true,
  maxRetryTime: 5 * 60 * 1000, // 5 minutes max
});

Timeout Generation

Generate arrays of timeout values for exponential backoff strategies. Used internally by operations but also available for custom timeout management.

/**
 * Generates array of timeout values for exponential backoff
 * @param {Object|Array} [options] - Timeout configuration or array of timeout values
 * @param {number} [options.retries=10] - Maximum number of retries
 * @param {number} [options.factor=2] - Exponential backoff factor
 * @param {number} [options.minTimeout=1000] - Minimum timeout in milliseconds
 * @param {number} [options.maxTimeout=Infinity] - Maximum timeout in milliseconds
 * @param {boolean} [options.randomize=false] - Add randomization to timeouts
 * @param {boolean} [options.forever=false] - Enable infinite retries
 * @returns {number[]} Array of timeout values in milliseconds
 */
function timeouts(options);

/**
 * Calculates timeout value for specific attempt number
 * @param {number} attempt - Zero-indexed attempt number
 * @param {Object} opts - Options containing factor, minTimeout, maxTimeout, randomize
 * @param {number} opts.factor - Exponential backoff factor
 * @param {number} opts.minTimeout - Minimum timeout in milliseconds
 * @param {number} opts.maxTimeout - Maximum timeout in milliseconds
 * @param {boolean} [opts.randomize=false] - Add randomization to timeout
 * @returns {number} Timeout value in milliseconds
 */
function createTimeout(attempt, opts);

Usage Examples:

const retry = require("retry");

// Generate timeout array
const timeouts = retry.timeouts({
  retries: 3,
  factor: 2,
  minTimeout: 1000,
  maxTimeout: 5000,
  randomize: true
});
// Result: [1000-2000, 2000-4000, 4000-5000] (randomized)

// Calculate specific timeout
const timeout = retry.createTimeout(2, {
  factor: 2,
  minTimeout: 1000,
  maxTimeout: 10000
});
// Result: 4000 (1000 * 2^2)

// Use array of custom timeouts
const customTimeouts = retry.timeouts([100, 500, 1000, 2000]);

Method Wrapping

Automatically add retry logic to existing object methods. Useful for wrapping APIs, database clients, or any object with methods that may fail.

/**
 * Wraps object methods with retry logic
 * @param {Object} obj - Object containing methods to wrap
 * @param {Object|Array} [options] - Retry options or method names array
 * @param {Array} [methods] - Array of method names to wrap
 * @returns {void} Modifies obj in place
 */
function wrap(obj, options, methods);

Usage Examples:

const retry = require("retry");
const dns = require("dns");

// Wrap all functions in an object
retry.wrap(dns);

// Wrap specific methods
retry.wrap(dns, ["resolve", "lookup"]);

// Wrap with custom retry options
retry.wrap(dns, {
  retries: 3,
  factor: 2,
  minTimeout: 1000
});

// Wrap specific methods with options
retry.wrap(dns, {
  retries: 5,
  maxTimeout: 10000
}, ["resolve"]);

// The wrapped methods now automatically retry on errors
dns.resolve("example.com", function(err, addresses) {
  // This will retry up to the configured number of times
  console.log(err, addresses);
});

RetryOperation Management

Direct management of RetryOperation instances for fine-grained control over retry behavior and error handling.

/**
 * RetryOperation constructor - manages individual retry operation lifecycle
 * @param {number[]} timeouts - Array of timeout values in milliseconds
 * @param {Object|boolean} [options] - Configuration options
 * @param {boolean} [options.forever=false] - Whether to retry forever
 * @param {boolean} [options.unref=false] - Whether to unref setTimeout calls
 * @param {number} [options.maxRetryTime=Infinity] - Maximum time allowed for retries
 */
function RetryOperation(timeouts, options);

/**
 * Executes function with retry capability
 * @param {Function} fn - Function to execute, receives currentAttempt as parameter
 * @param {Object} [timeoutOps] - Timeout configuration
 * @param {number} [timeoutOps.timeout] - Operation timeout in milliseconds
 * @param {Function} [timeoutOps.cb] - Timeout callback function
 * @returns {void}
 */
RetryOperation.prototype.attempt = function(fn, timeoutOps);

/**
 * Determines if operation should be retried based on error
 * @param {Error|null} err - Error from operation attempt
 * @returns {boolean} true if operation will be retried, false otherwise
 */
RetryOperation.prototype.retry = function(err);

/**
 * Stops retry operation and clears timers
 * @returns {void}
 */
RetryOperation.prototype.stop = function();

/**
 * Resets operation to initial state for reuse
 * @returns {void}
 */
RetryOperation.prototype.reset = function();

/**
 * Returns array of all errors encountered during retries
 * @returns {Error[]} Array of Error objects in chronological order
 */
RetryOperation.prototype.errors = function();

/**
 * Returns current attempt count
 * @returns {number} Number representing attempt count
 */
RetryOperation.prototype.attempts = function();

/**
 * Returns most frequently occurring error
 * @returns {Error|null} Error object or null if no errors
 */
RetryOperation.prototype.mainError = function();

/**
 * @deprecated Use attempt() instead
 * Deprecated alias for attempt()
 * @param {Function} fn - Function to execute
 * @returns {void}
 */
RetryOperation.prototype.try = function(fn);

/**
 * @deprecated Use attempt() instead  
 * Deprecated alias for attempt()
 * @param {Function} fn - Function to execute
 * @returns {void}
 */
RetryOperation.prototype.start = function(fn);

Usage Examples:

const retry = require("retry");

// Direct RetryOperation usage
const timeouts = retry.timeouts({ retries: 3 });
const operation = new retry.RetryOperation(timeouts);

operation.attempt(function(currentAttempt) {
  console.log(`Attempt ${currentAttempt}`);
  
  asyncOperation(function(err, result) {
    if (err && err.message === 'FATAL_ERROR') {
      // Stop retrying on fatal errors
      operation.stop();
      return callback(err);
    }
    
    if (operation.retry(err)) {
      return; // Will retry
    }
    
    // Final result (success or max retries reached)
    if (err) {
      console.log('All errors:', operation.errors());
      console.log('Main error:', operation.mainError());
      console.log('Total attempts:', operation.attempts());
    }
    
    callback(operation.mainError(), result);
  });
});

// Reset and reuse operation
operation.reset();
operation.attempt(/* ... */);

Types

/**
 * Configuration options for retry operations
 * @typedef {Object} RetryOptions
 * @property {number} [retries=10] - Maximum number of retry attempts
 * @property {number} [factor=2] - Exponential backoff factor
 * @property {number} [minTimeout=1000] - Minimum timeout in milliseconds
 * @property {number} [maxTimeout=Infinity] - Maximum timeout in milliseconds
 * @property {boolean} [randomize=false] - Add randomization to timeouts
 * @property {boolean} [forever=false] - Whether to retry forever
 * @property {boolean} [unref=false] - Whether to unref setTimeout calls
 * @property {number} [maxRetryTime=Infinity] - Maximum time allowed for retries in milliseconds
 */

/**
 * Timeout operation configuration
 * @typedef {Object} TimeoutOps
 * @property {number} timeout - Operation timeout in milliseconds
 * @property {Function} cb - Timeout callback function
 */

Error Handling

The retry library provides comprehensive error handling and analysis:

Error Collection

  • All retry attempts collect errors in chronological order via errors()
  • mainError() returns the most frequently occurring error based on error message
  • Error aggregation supports detailed failure analysis

Special Error Behaviors

  • Timeout errors: Automatically added when maxRetryTime is exceeded
  • Fatal error handling: Use operation.stop() to abort retries for unrecoverable errors
  • Forever mode: When forever: true, only keeps the last error to prevent memory leaks

Timeout Formula

The timeout calculation uses the following formula:

timeout = Math.min(random * Math.max(minTimeout, 1) * Math.pow(factor, attempt), maxTimeout)

Where random is 1 or a value between 1-2 if randomize is enabled. The Math.max(minTimeout, 1) ensures a minimum timeout of at least 1 millisecond.

Common Patterns

DNS Resolution with Retry

const dns = require("dns");
const retry = require("retry");

function faultTolerantResolve(address, cb) {
  const operation = retry.operation({
    retries: 2,
    factor: 2,
    minTimeout: 1 * 1000,
    maxTimeout: 2 * 1000,
    randomize: true
  });

  operation.attempt(function(currentAttempt) {
    dns.resolve(address, function(err, addresses) {
      if (operation.retry(err)) {
        return;
      }

      cb(operation.mainError(), operation.errors(), addresses);
    });
  });
}

API Client with Fatal Error Handling

function apiCall(endpoint, data, callback) {
  const operation = retry.operation({ retries: 3 });
  
  operation.attempt(function(currentAttempt) {
    httpRequest(endpoint, data, function(err, response) {
      if (err && err.statusCode === 401) {
        // Don't retry authentication errors
        operation.stop();
        return callback(err);
      }
      
      if (operation.retry(err)) {
        return;
      }
      
      callback(operation.mainError(), response);
    });
  });
}

Database Connection with Wrapping

const retry = require("retry");

// Wrap database client methods
retry.wrap(dbClient, {
  retries: 5,
  factor: 1.5,
  minTimeout: 500,
  maxTimeout: 10000
}, ["query", "connect"]);

// Now database operations automatically retry
dbClient.query("SELECT * FROM users", function(err, results) {
  // This query will retry up to 5 times on failure
  console.log(err, results);
});