jbzoo/retry

Tiny PHP library providing retry functionality with multiple backoff strategies and jitter support

7.0.2 2025-09-27 21:35 UTC

This package is auto-updated.

Last update: 2025-09-27 21:36:37 UTC


README

CI Coverage Status Psalm Coverage Psalm Level CodeFactor Stable Version Total Downloads Dependents GitHub License

Tiny PHP library providing retry functionality with multiple backoff strategies and jitter support.

Features

  • 4 retry strategies (plus the ability to use your own)
  • Optional jitter/randomness to spread out retries and minimize collisions
  • Wait time cap to limit maximum retry delays
  • Custom callbacks for retry logic and error handling
  • Type-safe with strict typing and comprehensive test coverage
  • Backward compatible with stechstudio/backoff

Requirements

  • PHP 8.2 or higher

About This Fork

This library is a modernized fork of stechstudio/backoff with several improvements:

  • Strict typing and comprehensive test coverage
  • Explicit configuration instead of global static defaults
  • Better naming - "retry" terminology instead of "backoff"
  • Enhanced jitter control with setJitterPercent() and setJitterMinCap() methods
  • Backward compatibility through aliases

Installation

composer require jbzoo/retry

Quick Start

This library provides sane defaults for immediate use. By default: quadratic strategy with 100ms base time (attempt^2 * 100), maximum 5 retries, and no jitter.

Simple Function Usage

The simplest way to use Retry is with the retry helper function:

use function JBZoo\Retry\retry;

$result = retry(function() {
    return doSomeWorkThatMightFail();
});

If successful $result will contain the result of the closure. If max attempts are exceeded the inner exception is re-thrown.

You can of course provide other options via the helper method if needed.

Parameters: $callback, $maxAttempts, $strategy, $waitCap, $useJitter.

Class-Based Usage

Constructor parameters: $maxAttempts, $strategy, $waitCap, $useJitter.

use JBZoo\Retry\Retry;

$retry = new Retry(10, 'exponential', 10000, true);
$result = $retry->run(function() {
    return doSomeWorkThatMightFail();
});

Fluent Interface

For dependency injection scenarios, use chainable setters:

use JBZoo\Retry\Retry;

$result = (new Retry())
    ->setStrategy('constant')
    ->setMaxAttempts(10)
    ->enableJitter()
    ->run(function() {
        return doSomeWorkThatMightFail();
    });

Configuration Philosophy

This library enforces explicit configuration over global defaults. Unlike the original library, static configuration variables are deprecated and disabled. This design choice ensures:

  • Different parts of your project can have completely different retry settings
  • No conflicts with third-party libraries using their own defaults
  • Clear, explicit dependency injection patterns

Use dependency injection or direct instantiation instead of global configuration.

Retry Strategies

Four built-in strategies are available, each with a default base time of 100 milliseconds:

Constant Strategy

Sleeps for a fixed time on each retry.

use JBZoo\Retry\Strategies\ConstantStrategy;
$strategy = new ConstantStrategy(500); // 500ms each retry

Linear Strategy

Sleep time increases linearly: attempt × baseTime.

use JBZoo\Retry\Strategies\LinearStrategy;
$strategy = new LinearStrategy(200); // 200ms, 400ms, 600ms...

Polynomial Strategy

Sleep time follows polynomial growth: (attempt^degree) × baseTime.

use JBZoo\Retry\Strategies\PolynomialStrategy;
$strategy = new PolynomialStrategy(100, 3); // (attempt^3) × 100ms
// Default degree is 2 (quadratic): 100ms, 400ms, 900ms...

Exponential Strategy

Sleep time grows exponentially: (2^attempt) × baseTime.

use JBZoo\Retry\Strategies\ExponentialStrategy;
$strategy = new ExponentialStrategy(100); // 200ms, 400ms, 800ms...

Strategy Usage Options

String-Based Configuration

use JBZoo\Retry\Retry;
use function JBZoo\Retry\retry;

retry(fn() => doWork(), 10, 'constant'); // Uses ConstantStrategy with 100ms default
$retry = new Retry(10, 'constant');

Instance-Based Configuration

use JBZoo\Retry\Retry;
use JBZoo\Retry\Strategies\LinearStrategy;
use function JBZoo\Retry\retry;

retry(fn() => doWork(), 10, new LinearStrategy(500));
$retry = new Retry(10, new LinearStrategy(500));

Integer-Based Configuration

Passing an integer creates a ConstantStrategy with that base time:

retry(fn() => doWork(), 10, 1000); // 1000ms constant delay
$retry = new Retry(10, 1000);

Custom Closure Strategy

Define your own strategy with a closure:

// Closure receives attempt number and returns sleep time in milliseconds
retry(fn() => doWork(), 10, fn($attempt) => (100 * $attempt) + 5000);

$retry = new Retry(10);
$retry->setStrategy(fn($attempt) => (100 * $attempt) + 5000);

Wait Cap

Limit maximum wait time for fast-growing strategies (like exponential):

retry(fn() => doWork(), 10, 'exponential', 5000); // Cap at 5 seconds
$retry = new Retry()->setWaitCap(5000);

Jitter

Prevent retry collisions by adding randomness to wait times. This is crucial when multiple clients might retry simultaneously.

retry(fn() => doWork(), 10, 'exponential', null, true); // Enable jitter
$retry = new Retry()->enableJitter();

Advanced Jitter Control

Fine-tune jitter behavior with additional methods:

$retry = new Retry()
    ->enableJitter()
    ->setJitterPercent(75)    // Use 75% of calculated wait time as max
    ->setJitterMinCap(100);   // Minimum jitter time of 100ms

By default, this library uses "FullJitter" - a random time between 0 and the calculated wait time. See AWS's excellent explanation for more details.

Advanced Usage

Custom Retry Logic

Implement custom retry conditions beyond simple exception handling:

use JBZoo\Retry\Retry;

$retry = new Retry();
$retry->setDecider(function($attempt, $maxAttempts, $result, $exception = null) {
    // Custom logic: retry based on time, specific exceptions, return values, etc.
    return $attempt < 3 && ($exception instanceof SpecificException);
});

Error Handling

Add logging or monitoring for retry attempts:

use JBZoo\Retry\Retry;

$retry = new Retry();
$retry->setErrorHandler(function($exception, $attempt, $maxAttempts) {
    error_log("Retry {$attempt}/{$maxAttempts}: {$exception->getMessage()}");
});

Development

Running Tests

make update    # Install dependencies
make test      # Run PHPUnit tests
make codestyle # Run code quality checks
make test-all  # Run both tests and code style

License

MIT

See Also

  • CI-Report-Converter - Converting different error reports for deep compatibility with popular CI systems.
  • Composer-Diff - See what packages have changed after composer update.
  • Composer-Graph - Dependency graph visualization of composer.json based on mermaid-js.
  • Mermaid-PHP - Generate diagrams and flowcharts with the help of the mermaid script language.
  • Utils - Collection of useful PHP functions, mini-classes, and snippets for every day.
  • Image - Package provides object-oriented way to manipulate with images as simple as possible.
  • Data - Extended implementation of ArrayObject. Use files as config/array.
  • SimpleTypes - Converting any values and measures - money, weight, exchange rates, length, ...