#
Retrying Requests
Sometimes you may deal with APIs that fail frequently because of network issues or temporary server errors. Fansipan has a useful built-in feature that allows you to send a request and retry multiple times.
#
Getting Started
To retry a failed request, you should use the Fansipan\ConnectorConfigurator
to configure your connector for retrying the request. The retry
method accepts the maximum number of times the request should be attempted, a retry strategy to decide if the request should be retried, and to define the waiting time between each retry.
use Fansipan\ConnectorConfigurator;
$connector = new MyConnector();
$response = (new ConnectorConfigurator())
->retry()
->configure($connector)
->send(new MyRequest());
// or retries for 5 times
$response = (new ConnectorConfigurator())
->retry(5)
->configure($connector)
->send(new MyRequest());
#
Customising When a Retry Is Attempted
By default, failed requests are retried up to 3 times, with an exponential delay between retries (first retry = 1 second; second retry: 2 seconds, third retry: 4 seconds) and only for the following HTTP status codes: 423
, 425
, 429
, 502
and 503
when using any HTTP method and 500
, 504
, 507
and 510
when using an HTTP idempotent method.
If needed, you may pass a second argument to the retry
method. It is an instance of Fansipan\Contracts\RetryStrategyInterface
that determines if the retries should actually be attempted. This will retries the failed requests with a delay of 1 second.
use Fansipan\ConnectorConfigurator;
use Fansipan\Retry\RetryCallback;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
$response = (new ConnectorConfigurator())
->retry(5, RetryCallback::when(static function (RequestInterface $request, ResponseInterface $response) {
return $response->getStatusCode() >= 500;
}))
->configure(new MyConnector())
->send(new MyRequest());
#
Customising Delay
You may also pass second and third arguments to the RetryCallback::when()
method to customise the waiting time between each retry.
use Fansipan\ConnectorConfigurator;
use Fansipan\Retry\RetryCallback;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
$response = (new ConnectorConfigurator())
->retry(3, RetryCallback::when(static function (RequestInterface $request, ResponseInterface $response) {
// Your logic here
}, delay: 1000, multiplier: 2.0))
->configure(new MyConnector())
->send(new MyRequest());
In the example above, failed requests are retried up to 3 times, with an exponential delay between retries (first retry = 1 second; second retry: 2 seconds, third retry: 4 seconds).
Instead of using an interval delay or calculated exponential delay, you may easily configure "exponential" backoffs by using withDelay()
method. In this example, the retry delay will be 1 second for the first retry, 3 seconds for the second retry, and 10 seconds for the third retry:
use Fansipan\ConnectorConfigurator;
use Fansipan\Retry\Backoff;
use Fansipan\Retry\RetryCallback;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
$response = (new ConnectorConfigurator())
->retry(3, RetryCallback::when(static function (RequestInterface $request, ResponseInterface $response) {
// Your logic here
})->withDelay(new Backoff([1, 3, 10]))
->configure(new MyConnector())
->send(new MyRequest());
#
Disabling Throwing Exceptions
If a request fails, it will be attempted again - if it reaches the maximum number of errors, a Fansipan\Exception\RequestRetryFailedException
will be thrown. If a request is successful at any point, it will return a Fansipan\Response
instance.
If you would like to disable this behavior, you may provide a throw
argument with a value of false
. When disabled, the last response received by the client will be returned after all retries have been attempted:
use Fansipan\ConnectorConfigurator;
$response = (new ConnectorConfigurator())
->retry(2, null, throw: false)
->configure(new MyConnector())
->send(new MyRequest());
#
Retrying All Requests Globally
Since middleware is mutable, adding new middleware means that all subsequent requests will also have it applied.
<?php
use Fansipan\Contracts\ConnectorInterface;
use Fansipan\Traits\ConnectorTrait;
final class MyConnector implements ConnectorInterface
{
use ConnectorTrait;
}
use Fansipan\Middleware\RetryRequests;
use Fansipan\Retry\Delay;
use Fansipan\Retry\GenericRetryStrategy;
$connector = new MyConnector();
$connector->middleware()->unshift(new RetryRequests(
new GenericRetryStrategy(new Delay(1000, 2.0)),
));
$response = $connector->send(new MyRequest());
// or always retries for 5 times
$connector->middleware()->unshift(new RetryRequests(
new GenericRetryStrategy(new Delay(1000, 2.0)),
5
));
$response = $connector->send(new MyRequest());