• Stars
    star
    187
  • Rank 206,464 (Top 5 %)
  • Language
    PHP
  • License
    MIT License
  • Created about 8 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

PHP Backoff

Latest Version on Packagist Build Software License Quality Score Total Downloads

Easily wrap your code with retry functionality. This library provides:

  1. 4 backoff strategies (plus the ability to use your own)
  2. Optional jitter / randomness to spread out retries and minimize collisions
  3. Wait time cap
  4. Callbacks for custom retry logic or error handling

Installation

composer require stechstudio/backoff

Defaults

This library provides sane defaults so you can hopefully just jump in for most of your use cases.

By default the backoff is quadratic with a 100ms base time (attempt^2 * 100), a max of 5 retries, and no jitter.

Quickstart

The simplest way to use Backoff is with the global backoff helper function:

$result = backoff(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.

Method parameters are $callback, $maxAttempts, $strategy, $waitCap, $useJitter.

Backoff class usage

The Backoff class constructor parameters are $maxAttempts, $strategy, $waitCap, $useJitter.

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

Or if you are injecting the Backoff class with a dependency container, you can set it up with setters after the fact. Note that setters are chainable.

// Assuming a fresh instance of $backoff was handed to you
$result = $backoff
    ->setStrategy('constant')
    ->setMaxAttempts(10)
    ->enableJitter()
    ->run(function() {
        return doSomeWorkThatMightFail();
    });

Changing defaults

If you find you want different defaults, you can modify them via static class properties:

Backoff::$defaultMaxAttempts = 10;
Backoff::$defaultStrategy = 'exponential';
Backoff::$defaultJitterEnabled = true;

You might want to do this somewhere in your application bootstrap for example. These defaults will be used anytime you create an instance of the Backoff class or use the backoff() helper function.

Strategies

There are four built-in strategies available: constant, linear, polynomial, and exponential.

The default base time for all strategies is 100 milliseconds.

Constant

$strategy = new ConstantStrategy(500);

This strategy will sleep for 500 milliseconds on each retry loop.

Linear

$strategy = new LinearStrategy(200);

This strategy will sleep for attempt * baseTime, providing linear backoff starting at 200 milliseconds.

Polynomial

$strategy = new PolynomialStrategy(100, 3);

This strategy will sleep for (attempt^degree) * baseTime, so in this case (attempt^3) * 100.

The default degree if none provided is 2, effectively quadratic time.

Exponential

$strategy = new ExponentialStrategy(100);

This strategy will sleep for (2^attempt) * baseTime.

Specifying strategy

In our earlier code examples we specified the strategy as a string:

backoff(function() {
    ...
}, 10, 'constant');

// OR

$backoff = new Backoff(10, 'constant');

This would use the ConstantStrategy with defaults, effectively giving you a 100 millisecond sleep time.

You can create the strategy instance yourself in order to modify these defaults:

backoff(function() {
    ...
}, 10, new LinearStrategy(500));

// OR

$backoff = new Backoff(10, new LinearStrategy(500));

You can also pass in an integer as the strategy, will translates to a ConstantStrategy with the integer as the base time in milliseconds:

backoff(function() {
    ...
}, 10, 1000);

// OR

$backoff = new Backoff(10, 1000);

Finally, you can pass in a closure as the strategy if you wish. This closure should receive an integer attempt and return a sleep time in milliseconds.

backoff(function() {
    ...
}, 10, function($attempt) {
    return (100 * $attempt) + 5000;
});

// OR

$backoff = new Backoff(10);
$backoff->setStrategy(function($attempt) {
    return (100 * $attempt) + 5000;
});

Wait cap

You may want to use a fast growing backoff time (like exponential) but then also set a max wait time so that it levels out after a while.

This cap can be provided as the fourth argument to the backoff helper function, or using the setWaitCap() method on the Backoff class.

Jitter

If you have a lot of clients starting a job at the same time and encountering failures, any of the above backoff strategies could mean the workers continue to collide at each retry.

The solution for this is to add randomness. See here for a good explanation:

https://www.awsarchitectureblog.com/2015/03/backoff.html

You can enable jitter by passing true in as the fifth argument to the backoff helper function, or by using the enableJitter() method on the Backoff class.

We use the "FullJitter" approach outlined in the above article, where a random number between 0 and the sleep time provided by your selected strategy is used.

Custom retry decider

By default Backoff will retry if an exception is encountered, and if it has not yet hit max retries.

You may provide your own retry decider for more advanced use cases. Perhaps you want to retry based on time rather than number of retries, or perhaps there are scenarios where you would want retry even when an exception was not encountered.

Provide the decider as a callback, or an instance of a class with an __invoke method. Backoff will hand it four parameters: the current attempt, max attempts, the last result received, and the exception if one was encountered. Your decider needs to return true or false.

$backoff->setDecider(function($attempt, $maxAttempts, $result, $exception = null) {
    return someCustomLogic();
});

Error handler callback

You can provide a custom error handler to be notified anytime an exception occurs, even if we have yet to reach max attempts. This is a useful place to do logging for example.

$backoff->setErrorHandler(function($exception, $attempt, $maxAttempts) {
    Log::error("On run $attempt we hit a problem: " . $exception->getMessage());
});

More Repositories

1

laravel-zipstream

Easily create Zip files on-the-fly and provide a streaming download
PHP
381
star
2

filament-impersonate

Filament plugin that makes it easy to impersonate your users
PHP
232
star
3

laravel-ssh-tunnel

Easy creation & maintenance of an SSH Tunnel for Laravel/Lumen
PHP
191
star
4

Laravel-PHP-CS-Fixer

Artisan Command for FriendsOfPHP/PHP-CS_Fixer
PHP
146
star
5

laravel-jwt

Helper package that makes it easy to generate, consume, and protect routes with JWT tokens in Laravel
PHP
121
star
6

laravel-env-security

Securely manage Laravel .env files for different deployment environments
PHP
75
star
7

laravel-bref-bridge

Bref, the Laravel way.
PHP
71
star
8

laravel-metrics

Easily track metrics from Laravel events and ship to InfluxDB, Prometheus, CloudWatch, or PostHog.
PHP
46
star
9

libvips-lambda

libvips Executable for AWS Lambda
Shell
46
star
10

aws-lambda-build

Docker Image for building AWS Lambda executables.
Dockerfile
40
star
11

laravel-aws-lambda

⛔️ DEPRECATED: Running Laravel on AWS Lambda
PHP
33
star
12

laravel-visual-testing

Visual UI screenshot testing with Laravel Dusk using percy.io
PHP
32
star
13

php-lambda

Build PHP executables for AWS Lambda
Dockerfile
24
star
14

laravel-record

What if Laravel's Collection and Model classes had a baby?
PHP
22
star
15

laravel-raw-sessions

Laravel session driver to establish bridge with raw PHP $_SESSION
PHP
18
star
16

laravel-hubspot

PHP
17
star
17

bref-extensions

Build tools for compiling binaries and extensions against the bref layer.
Dockerfile
14
star
18

codeception-laravel-unittest

PHP
11
star
19

laravel-vfs-adapter

Virtual Filesystem Storage Adapter for Laravel
PHP
10
star
20

filament-opcache

Manage OPcache from your Filament admin panel.
PHP
7
star
21

laravel-socialite-auth

PHP
6
star
22

filament-phpinfo

View phpinfo from your Filament admin panel.
Blade
4
star
23

phpinfo

Easily interact with your PHP configuration
PHP
4
star
24

laravel-upload-server

PHP
3
star
25

chalice_helpers

Some utilities for AWS Chalice
Python
3
star
26

aws-events

Library to help manage the plethora of event configurations in AWS Lambda
PHP
3
star
27

bai2

PHP library for parsing BAI2 files
PHP
2
star
28

laravel-storage-connect

Laravel package to easily connect to cloud storage accounts and upload files
PHP
2
star
29

insult-service

Random Insults from Martin Luther
JavaScript
1
star
30

laravatar

Minimalist Blade component for displaying a Gravatar, or falling back to initials.
PHP
1
star
31

laravel-locking-migrations

PHP
1
star
32

laravel-email-events

Laravel package to normalize webhooks from email providers like SendGrid or Postmark
PHP
1
star
33

lbb-core

Laravel Vapor Core Lambda Bootstrap for your own Lambda Layer, like bref!
PHP
1
star
34

slack-laravel-api

Laravelized Slack PHP Api
PHP
1
star