{"id":23003751,"url":"https://github.com/stechstudio/backoff","last_synced_at":"2025-04-12T15:43:57.473Z","repository":{"id":39953975,"uuid":"71412590","full_name":"stechstudio/backoff","owner":"stechstudio","description":"PHP library providing retry functionality with multiple backoff strategies and jitter support","archived":false,"fork":false,"pushed_at":"2024-03-29T12:05:04.000Z","size":34,"stargazers_count":180,"open_issues_count":0,"forks_count":21,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-05-17T00:45:42.150Z","etag":null,"topics":["backoff","jitter","php","retry"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stechstudio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-20T01:02:40.000Z","updated_at":"2024-06-18T12:35:26.854Z","dependencies_parsed_at":"2024-03-07T22:22:50.470Z","dependency_job_id":"86d5eb04-f752-42de-bbc3-147f46b382b7","html_url":"https://github.com/stechstudio/backoff","commit_stats":{"total_commits":25,"total_committers":6,"mean_commits":4.166666666666667,"dds":0.28,"last_synced_commit":"c1182e65d322be640d20e28296f6b4f436f6cea6"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stechstudio%2Fbackoff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stechstudio%2Fbackoff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stechstudio%2Fbackoff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stechstudio%2Fbackoff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stechstudio","download_url":"https://codeload.github.com/stechstudio/backoff/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248590985,"owners_count":21129927,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backoff","jitter","php","retry"],"created_at":"2024-12-15T07:15:13.398Z","updated_at":"2025-04-12T15:43:57.455Z","avatar_url":"https://github.com/stechstudio.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PHP Backoff\n\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/stechstudio/backoff.svg?style=flat-square)](https://packagist.org/packages/stechstudio/backoff)\n![Tests](https://img.shields.io/github/actions/workflow/status/stechstudio/backoff/run-tests.yml?style=flat-square)\n[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)\n[![Total Downloads](https://img.shields.io/packagist/dt/stechstudio/backoff.svg?style=flat-square)](https://packagist.org/packages/stechstudio/backoff)\n\nEasily wrap your code with retry functionality. This library provides:\n\n 1. 4 backoff strategies (plus the ability to use your own)\n 2. Optional jitter / randomness to spread out retries and minimize collisions\n 3. Wait time cap\n 4. Callbacks for custom retry logic or error handling\n\n## Installation\n\n```\ncomposer require stechstudio/backoff\n```\n\n## Defaults\n\nThis library provides sane defaults so you can hopefully just jump in for most of your use cases.\n\nBy default the backoff is quadratic with a 100ms base time (`attempt^2 * 100`), a max of 5 retries, and no jitter.\n\n## Quickstart\n\nThe simplest way to use Backoff is with the global `backoff` helper function:\n\n```php\n$result = backoff(function() {\n    return doSomeWorkThatMightFail();\n});\n```\n\nIf successful `$result` will contain the result of the closure. If max attempts are exceeded the inner exception is re-thrown.\n\nYou can of course provide other options via the helper method if needed.\n\nMethod parameters are `$callback`, `$maxAttempts`, `$strategy`, `$waitCap`, `$useJitter`.\n\n## Backoff class usage\n\nThe Backoff class constructor parameters are `$maxAttempts`, `$strategy`, `$waitCap`, `$useJitter`.\n\n```php\n$backoff = new Backoff(10, 'exponential', 10000, true);\n$result = $backoff-\u003erun(function() {\n    return doSomeWorkThatMightFail();\n});\n```\n\nOr 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.\n\n```php\n// Assuming a fresh instance of $backoff was handed to you\n$result = $backoff\n    -\u003esetStrategy('constant')\n    -\u003esetMaxAttempts(10)\n    -\u003eenableJitter()\n    -\u003erun(function() {\n        return doSomeWorkThatMightFail();\n    });\n```\n\n## Changing defaults\n\nIf you find you want different defaults, you can modify them via static class properties:\n\n```php\nBackoff::$defaultMaxAttempts = 10;\nBackoff::$defaultStrategy = 'exponential';\nBackoff::$defaultJitterEnabled = true;\n```\n\nYou 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.\n\n## Strategies\n\nThere are four built-in strategies available: constant, linear, polynomial, and exponential.\n\nThe default base time for all strategies is 100 milliseconds.\n\n### Constant\n\n```php\n$strategy = new ConstantStrategy(500);\n```\n\nThis strategy will sleep for 500 milliseconds on each retry loop.\n\n### Linear\n\n```php\n$strategy = new LinearStrategy(200);\n```\n\nThis strategy will sleep for `attempt * baseTime`, providing linear backoff starting at 200 milliseconds.\n\n### Polynomial\n\n```php\n$strategy = new PolynomialStrategy(100, 3);\n```\n\nThis strategy will sleep for `(attempt^degree) * baseTime`, so in this case `(attempt^3) * 100`.\n\nThe default degree if none provided is 2, effectively quadratic time.\n\n### Exponential\n\n```php\n$strategy = new ExponentialStrategy(100);\n```\n\nThis strategy will sleep for `(2^attempt) * baseTime`.\n\n## Specifying strategy\n\nIn our earlier code examples we specified the strategy as a string:\n\n```php\nbackoff(function() {\n    ...\n}, 10, 'constant');\n\n// OR\n\n$backoff = new Backoff(10, 'constant');\n```\n\nThis would use the `ConstantStrategy` with defaults, effectively giving you a 100 millisecond sleep time.\n\nYou can create the strategy instance yourself in order to modify these defaults:\n\n```php\nbackoff(function() {\n    ...\n}, 10, new LinearStrategy(500));\n\n// OR\n\n$backoff = new Backoff(10, new LinearStrategy(500));\n```\n\nYou can also pass in an integer as the strategy, will translates to a ConstantStrategy with the integer as the base time in milliseconds:\n\n```php\nbackoff(function() {\n    ...\n}, 10, 1000);\n\n// OR\n\n$backoff = new Backoff(10, 1000);\n```\n\nFinally, 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.\n\n```php\nbackoff(function() {\n    ...\n}, 10, function($attempt) {\n    return (100 * $attempt) + 5000;\n});\n\n// OR\n\n$backoff = new Backoff(10);\n$backoff-\u003esetStrategy(function($attempt) {\n    return (100 * $attempt) + 5000;\n});\n```\n\n## Wait cap\n\nYou 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.\n\nThis cap can be provided as the fourth argument to the `backoff` helper function, or using the `setWaitCap()` method on the Backoff class.\n\n## Jitter\n\nIf 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.\n\nThe solution for this is to add randomness. See here for a good explanation:\n\nhttps://www.awsarchitectureblog.com/2015/03/backoff.html\n\nYou 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.\n\nWe 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.\n\n## Custom retry decider\n\nBy default Backoff will retry if an exception is encountered, and if it has not yet hit max retries.\n\nYou 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.\n\nProvide 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.\n\n```php\n$backoff-\u003esetDecider(function($attempt, $maxAttempts, $result, $exception = null) {\n    return someCustomLogic();\n});\n```\n\n## Error handler callback\n\nYou 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.\n\n```php\n$backoff-\u003esetErrorHandler(function($exception, $attempt, $maxAttempts) {\n    Log::error(\"On run $attempt we hit a problem: \" . $exception-\u003egetMessage());\n});\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstechstudio%2Fbackoff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstechstudio%2Fbackoff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstechstudio%2Fbackoff/lists"}