Skip to content

Commit 037fbf1

Browse files
committed
Create README.md
1 parent 6606c34 commit 037fbf1

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed

README.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Retry
2+
3+
Retry is a PHP library for retrying operations with customizable backoff, jitter, and support for both synchronous and asynchronous APIs. It also allows for offline retrying and serialization.
4+
5+
## Features
6+
7+
- Support for both synchronous and asynchronous operations
8+
- Configurable delays: constant or exponential
9+
- Jitter to prevent synchronization of retry attempts
10+
- Halting on irrecoverable errors
11+
- Offline retry capability
12+
- Explicit serialization support
13+
14+
## Synchronous API
15+
16+
``` php
17+
use Mention\Retry\Retry;
18+
use GuzzleHttp\Client;
19+
20+
$result = Retry::sync()->retry($operation);
21+
```
22+
23+
`$operation` should be a callable, and will be called repeatedly until either:
24+
25+
- The function executes successfully (returns without exceptions),
26+
- A PermanentError is thrown indicating a retry would have the same outcome,
27+
- The RetryStrategy halted the retrying process after a certain number of retries or elapsed time
28+
29+
On success, `Retry::sync()->retry($operation)` returns the value of `$operation()`.
30+
31+
The following example will retry until `$client->get('http://example.com/')` is successful, or 1 minute has elasped (as per the default RetryStrategy) :
32+
33+
``` php
34+
use Mention\Retry\Retry;
35+
use GuzzleHttp\Client;
36+
37+
$response = Retry::sync()->retry(function() {
38+
$client = new Guzzlehttp\Client();
39+
return $client->get('http://example.com/');
40+
});
41+
```
42+
43+
## Asynchronous API
44+
45+
The async API works similarly, but expects the operation to return a `React\PromiseInterface`:
46+
47+
``` php
48+
use Mention\Retry\Retry;
49+
50+
Retry::async($loop)->retry(function() {
51+
$browser = new React\Http\Browser();
52+
return $browser->get('http://example.com/');
53+
});
54+
```
55+
56+
## Permanent errors
57+
58+
Sometimes it can be determined that retrying will not work. For example, it is unlikely that retrying after receiving an HTTP 4xx error will change the outcome.
59+
60+
In these cases the operation can halt the retry process by throwing a `Mention\Retry\PermanentError`. When this exceptions is thrown, `retry()` stops retrying and re-throws either the inner exception if any, or the PermanentError itself.
61+
62+
Example:
63+
64+
``` php
65+
use Mention\Retry\Retry;
66+
use Mention\Retry\PermanentError;
67+
use GuzzleHttp\Client;
68+
use GuzzleHttp\Exception\ClientException;
69+
70+
$response = Retry::sync()->retry(function() {
71+
$client = new Guzzlehttp\Client();
72+
try {
73+
return $client->get('http://example.com/404');
74+
} catch (ClientException $e) {
75+
throw PermanentError::withException($e);
76+
}
77+
});
78+
```
79+
80+
## RetryStrategy
81+
82+
Both `Retry::sync()` and `Retry::async()` accept a `RetryStrategy` argument that can be used to configure the retry behavior.
83+
84+
``` php
85+
use Mention\Retry\RetryStrategy\RetryStrategyBuilder;
86+
use Mention\Kebab\Duration\Duration;
87+
88+
$strategy = RetryStrategyBuilder::exponentialInteractive()->withMaxElapsedTime(Duration::seconds(15));
89+
$response = Retry::sync($strategy)->retry($operation);
90+
```
91+
92+
### Default strategy
93+
94+
When no `RetryStrategy` is passed, a default strategy is used. It is configured as follows:
95+
96+
- The delay is exponential, starts at 10 milliseconds and increases by a factor of 1.5 after each attempt
97+
- A jitter of 0.5 is applied to the delay to help prevent operations from synchronising with each other and sending surges of requests
98+
- Retrying halts after 60 seconds have elasped without success
99+
100+
It is possible to change the default strategy by calling `Mention\Retry::setDefaultRetryStrategy()`:
101+
102+
``` php
103+
use Mention\Retry\RetryStrategy\RetryStrategyBuilder;
104+
use Mention\Kebab\Duration\Duration;
105+
106+
$defaultStrategy = RetryStrategyBuilder::exponentialInteractive()->withMaxElapsedTime(Duration::seconds(15));
107+
Retry::setDefaultRetryStrategy($defaultStrategy);
108+
```
109+
110+
It can be useful to set a different default retry strategy in different contexts. For example, code executing in a web context should retry for a shorter time than code executing in a batch context. Changing the default retry strategy can be used for exactly that.
111+
112+
In tests it can be useful to ignore the default and custom strategies. This can be done by calling `Mention\Retry::overrideRetryStrategy()`:
113+
114+
``` php
115+
use Mention\Retry\RetryStrategy\RetryStrategyBuilder;
116+
117+
$testStrategy = RetryStrategyBuilder::noDelay();
118+
Retry::overrideRetryStrategy($defaultStrategy);
119+
```
120+
121+
Use of `Retry::overrideRetryStrategy()` outside of tests is discouraged. Use `Retry::setDefaultRetryStrategy()` instead, or pass an explicit strategy when calling `Retry::sync()` and `Retry::async()`.
122+
123+
### Custom strategies
124+
125+
The `Mention\Retry\RetryStrategy\RetryStrategyBuilder` class allows you to build custom strategies and to customize the following parameters:
126+
127+
- The delay between two attempts
128+
- How much jitter should be applied to the delay
129+
- The maximum number of attempts
130+
- The maximum amount of elapsed time
131+
132+
The builder provides a number of predefine strategies that can be further customized. See https://github.com/mentionapp/retry/blob/main/src/RetryStrategy/RetryStrategyBuilder.php.
133+
134+
### Serialization
135+
136+
Retry strategies can be serialized by calling the `->toJsonString()` method and deserialized by calling `::fromJsonString()`. This is designed to be persisted for later usage, and future versions of the library will support strategies serialized with previous versions.
137+
138+
## Offline retrying
139+
140+
`RetryStrategy` is stateless and can be used standalone to implement offline retrying. For example, it can be used in a scheduled task system in order to decide if and when to re-schedule a failing task:
141+
142+
``` php
143+
try {
144+
$task->execute();
145+
} catch (\Exception $e) {
146+
$task->increaseFailures();
147+
$retryStrategy = $task->getRetryStrategy();
148+
if (!$retryStrategy->shouldRetry($task->getFailuesCount(), $task->getOriginalScheduleTime())) {
149+
throw $e;
150+
} else {
151+
$delay = $retryStrategy->getDelay($task->getFailuesCount());
152+
$task->rescheduleWithDelay($delay);
153+
}
154+
}
155+
```

0 commit comments

Comments
 (0)