'Symfony Retryable HttpClient and timeout option. How it works when used together?
In the new Symfomy 5.2 a Retryable HttpClient was implemented.
Failed requests can now be retried by default in case of failure.
However, I could not debug or understand how the timeout option is parsed in it.
For example:
$response = $client->request('POST', $url, [
'body' => $reqBody,
'timeout' => 45
]);
If the first request fails after 30 seconds, and the second attempt succeed after another 30 seconds, do I get a timeout error (considering both requests took 30 seconds, 30+30 = 60s)?
Or the timeout option for this case will be valid for every attempt?
If so, is there a way to set the global timeout of the request?
Solution 1:[1]
After some testing using webhook.site with some timeout options, I could see the timeout option refers to every request.
I could not find a way to set the total timeout, but at least I was able to avoid further retries if an specific time had elapsed.
For that, I created a custom strategy. I added the following to my framework.yaml file:
framework
http_client:
default_options:
retry_failed:
retry_strategy: App\MyStrategy
And under src/MyStrategy.php I did:
<?php
namespace App;
use Symfony\Component\HttpClient\Response\AsyncContext;
use Symfony\Component\HttpClient\Retry\GenericRetryStrategy;
use Symfony\Component\HttpClient\Retry\RetryStrategyInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
class MyStrategy extends GenericRetryStrategy implements RetryStrategyInterface
{
public const MAX_RESPONSE_TIME = 40; //total time with all attempts
public $totalTime = 0;
/**
* @inheritDoc
*/
public function shouldRetry(
AsyncContext $context,
?string $responseContent,
?TransportExceptionInterface $exception
): ?bool
{
$info = $context->getInfo();
$this->totalTime += $info['total_time'] ?? 0;
if ($this->totalTime >= self::MAX_RESPONSE_TIME) {
return false;
}
return parent::shouldRetry($context, $responseContent, $exception);
}
/**
* @inheritDoc
*/
public function getDelay(
AsyncContext $context,
?string $responseContent,
?TransportExceptionInterface $exception
): int
{
$delay = parent::getDelay($context, $responseContent, $exception);
$this->totalTime += $delay / 1000;
if ($this->totalTime >= self::MAX_RESPONSE_TIME) {
$context->cancel();
}
return $delay;
}
}
But the original problem still remains. If the first attempt fails in 30 seconds, a new one can still take up to 40 seconds to be completed, and the whole thing can take up to 70 seconds, instead of 40.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ricardo Martins |
