Skip to content

Commit

Permalink
Rename HttpCodeDecider and RetryHttpClient
Browse files Browse the repository at this point in the history
  • Loading branch information
jderusse committed Sep 17, 2020
1 parent 731b2d3 commit 923633a
Show file tree
Hide file tree
Showing 11 changed files with 83 additions and 122 deletions.
Expand Up @@ -17,6 +17,7 @@
use Symfony\Bundle\FullStack;
use Symfony\Component\Asset\Package;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
Expand Down Expand Up @@ -1471,43 +1472,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode)
->variableNode('md5')->end()
->end()
->end()
->arrayNode('retry_failed')
->fixXmlConfig('http_code')
->canBeEnabled()
->addDefaultsIfNotSet()
->beforeNormalization()
->always(function ($v) {
if (isset($v['backoff_service']) && (isset($v['delay']) || isset($v['multiplier']) || isset($v['max_delay']))) {
throw new \InvalidArgumentException('The "backoff_service" option cannot be used along with the "delay", "multiplier" or "max_delay" options.');
}
if (isset($v['decider_service']) && (isset($v['http_codes']))) {
throw new \InvalidArgumentException('The "decider_service" option cannot be used along with the "http_codes" options.');
}

return $v;
})
->end()
->children()
->scalarNode('backoff_service')->defaultNull()->info('service id to override the retry backoff entirely')->end()
->scalarNode('decider_service')->defaultNull()->info('service id to override the retry decider entirely')->end()
->arrayNode('http_codes')
->performNoDeepMerging()
->beforeNormalization()
->ifArray()
->then(function ($v) {
return array_filter(array_values($v));
})
->end()
->prototype('scalar')->end()
->info('A list of HTTP status code that triggers a retry')
->defaultValue([423, 425, 429, 500, 502, 503, 504, 507, 510])
->end()
->integerNode('max_retries')->defaultValue(3)->min(0)->end()
->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used)')->end()
->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries))')->end()
->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite)')->end()
->end()
->end()
->append($this->addHttpClientRetrySection())
->end()
->end()
->scalarNode('mock_response_factory')
Expand Down Expand Up @@ -1650,43 +1615,7 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode)
->variableNode('md5')->end()
->end()
->end()
->arrayNode('retry_failed')
->fixXmlConfig('http_code')
->canBeEnabled()
->addDefaultsIfNotSet()
->beforeNormalization()
->always(function ($v) {
if (isset($v['backoff_service']) && (isset($v['delay']) || isset($v['multiplier']) || isset($v['max_delay']))) {
throw new \InvalidArgumentException('The "backoff_service" option cannot be used along with the "delay", "multiplier" or "max_delay" options.');
}
if (isset($v['decider_service']) && (isset($v['http_codes']))) {
throw new \InvalidArgumentException('The "decider_service" option cannot be used along with the "http_codes" options.');
}

return $v;
})
->end()
->children()
->scalarNode('backoff_service')->defaultNull()->info('service id to override the retry backoff entirely')->end()
->scalarNode('decider_service')->defaultNull()->info('service id to override the retry decider entirely')->end()
->arrayNode('http_codes')
->performNoDeepMerging()
->beforeNormalization()
->ifArray()
->then(function ($v) {
return array_filter(array_values($v));
})
->end()
->prototype('scalar')->end()
->info('A list of HTTP status code that triggers a retry')
->defaultValue([423, 425, 429, 500, 502, 503, 504, 507, 510])
->end()
->integerNode('max_retries')->defaultValue(3)->min(0)->end()
->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used)')->end()
->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries))')->end()
->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite)')->end()
->end()
->end()
->append($this->addHttpClientRetrySection())
->end()
->end()
->end()
Expand All @@ -1696,6 +1625,50 @@ private function addHttpClientSection(ArrayNodeDefinition $rootNode)
;
}

private function addHttpClientRetrySection()
{
$root = new NodeBuilder();

return $root
->arrayNode('retry_failed')
->fixXmlConfig('http_code')
->canBeEnabled()
->addDefaultsIfNotSet()
->beforeNormalization()
->always(function ($v) {
if (isset($v['backoff_service']) && (isset($v['delay']) || isset($v['multiplier']) || isset($v['max_delay']))) {
throw new \InvalidArgumentException('The "backoff_service" option cannot be used along with the "delay", "multiplier" or "max_delay" options.');
}
if (isset($v['decider_service']) && (isset($v['http_codes']))) {
throw new \InvalidArgumentException('The "decider_service" option cannot be used along with the "http_codes" options.');
}

return $v;
})
->end()
->children()
->scalarNode('backoff_service')->defaultNull()->info('service id to override the retry backoff')->end()
->scalarNode('decider_service')->defaultNull()->info('service id to override the retry decider')->end()
->arrayNode('http_codes')
->performNoDeepMerging()
->beforeNormalization()
->ifArray()
->then(function ($v) {
return array_filter(array_values($v));
})
->end()
->prototype('integer')->end()
->info('A list of HTTP status code that triggers a retry')
->defaultValue([423, 425, 429, 500, 502, 503, 504, 507, 510])
->end()
->integerNode('max_retries')->defaultValue(3)->min(0)->end()
->integerNode('delay')->defaultValue(1000)->min(0)->info('Time in ms to delay (or the initial value when multiplier is used)')->end()
->floatNode('multiplier')->defaultValue(2)->min(1)->info('If greater than 1, delay will grow exponentially for each retry: (delay * (multiple ^ retries))')->end()
->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite)')->end()
->end()
;
}

private function addMailerSection(ArrayNodeDefinition $rootNode)
{
$rootNode
Expand Down
Expand Up @@ -64,7 +64,7 @@
use Symfony\Component\Form\FormTypeGuesserInterface;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\RetryHttpClient;
use Symfony\Component\HttpClient\RetryableHttpClient;
use Symfony\Component\HttpClient\ScopingHttpClient;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
Expand Down Expand Up @@ -2048,8 +2048,8 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder

private function registerHttpClientRetry(array $retryOptions, string $name, ContainerBuilder $container)
{
if (!class_exists(RetryHttpClient::class)) {
throw new LogicException('Retry failed request support cannot be enabled as the component is not installed in the right version. Try running "composer require symfony/http-client:^5.2".');
if (!class_exists(RetryableHttpClient::class)) {
throw new LogicException('Retry failed request support cannot be enabled as version 5.2+ of the HTTP Client component is required.');
}

if (null !== $retryOptions['backoff_service']) {
Expand All @@ -2069,7 +2069,7 @@ private function registerHttpClientRetry(array $retryOptions, string $name, Cont
$deciderReference = new Reference($retryOptions['decider_service']);
} else {
$retryServiceId = $name.'.retry.decider';
$retryDefinition = new ChildDefinition('http_client.retry.abstract_httpcode_decider');
$retryDefinition = new ChildDefinition('http_client.retry.abstract_httpstatuscode_decider');
$retryDefinition
->replaceArgument(0, $retryOptions['http_codes']);
$container->setDefinition($retryServiceId, $retryDefinition);
Expand All @@ -2078,15 +2078,9 @@ private function registerHttpClientRetry(array $retryOptions, string $name, Cont
}

$container
->register($name.'.retry', RetryHttpClient::class)
->setDecoratedService($name) // pass after TraceableHttpClient
->setArguments([
new Reference($name.'.retry.inner'),
$deciderReference,
$backoffReference,
$retryOptions['max_retries'],
new Reference('logger'),
])
->register($name.'.retry', RetryableHttpClient::class)
->setDecoratedService($name)
->setArguments([new Reference($name.'.retry.inner'), $deciderReference, $backoffReference, $retryOptions['max_retries'], new Reference('logger')])
->addTag('monolog.logger', ['channel' => 'http_client']);
}

Expand Down
Expand Up @@ -18,8 +18,7 @@
use Symfony\Component\HttpClient\HttplugClient;
use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Component\HttpClient\Retry\ExponentialBackOff;
use Symfony\Component\HttpClient\Retry\HttpCodeDecider;
use Symfony\Component\HttpClient\Retry\HttpHeaderBackOff;
use Symfony\Component\HttpClient\Retry\HttpStatusCodeDecider;
use Symfony\Contracts\HttpClient\HttpClientInterface;

return static function (ContainerConfigurator $container) {
Expand Down Expand Up @@ -60,12 +59,7 @@
abstract_arg('multiplier'),
abstract_arg('max delay ms'),
])
->set('http_client.retry.abstract_httpheader_backoff', HttpHeaderBackOff::class)
->abstract()
->args([
abstract_arg('fallback backOff'),
])
->set('http_client.retry.abstract_httpcode_decider', HttpCodeDecider::class)
->set('http_client.retry.abstract_httpstatuscode_decider', HttpStatusCodeDecider::class)
->abstract()
->args([
abstract_arg('http codes'),
Expand Down
Expand Up @@ -42,7 +42,7 @@
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\RetryHttpClient;
use Symfony\Component\HttpClient\RetryableHttpClient;
use Symfony\Component\HttpClient\ScopingHttpClient;
use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass;
use Symfony\Component\Messenger\Transport\TransportFactory;
Expand Down Expand Up @@ -1486,7 +1486,7 @@ public function testHttpClientOverrideDefaultOptions()

public function testHttpClientRetry()
{
if (!class_exists(RetryHttpClient::class)) {
if (!class_exists(RetryableHttpClient::class)) {
$this->expectException(LogicException::class);
}
$container = $this->createContainerFromFile('http_client_retry');
Expand All @@ -1497,7 +1497,7 @@ public function testHttpClientRetry()
$this->assertSame(0, $container->getDefinition('http_client.retry.exponential_backoff')->getArgument(2));
$this->assertSame(2, $container->getDefinition('http_client.retry')->getArgument(3));

$this->assertSame(RetryHttpClient::class, $container->getDefinition('foo.retry')->getClass());
$this->assertSame(RetryableHttpClient::class, $container->getDefinition('foo.retry')->getClass());
$this->assertSame(4, $container->getDefinition('foo.retry.exponential_backoff')->getArgument(1));
}

Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/HttpClient/CHANGELOG.md
Expand Up @@ -10,7 +10,7 @@ CHANGELOG
* added `MockResponse::getRequestMethod()` and `getRequestUrl()` to allow inspecting which request has been sent
* added `EventSourceHttpClient` a Server-Sent events stream implementing the [EventSource specification](https://www.w3.org/TR/eventsource/#eventsource)
* added option "extra.curl" to allow setting additional curl options in `CurlHttpClient`
* added `RetryHttpClient` to automatically retry failed requests.
* added `RetryableHttpClient` to automatically retry failed HTTP requests.

5.1.0
-----
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Component/HttpClient/Retry/ExponentialBackOff.php
Expand Up @@ -42,17 +42,17 @@ final class ExponentialBackOff implements RetryBackOffInterface
public function __construct(int $delayMilliseconds = 1000, float $multiplier = 2, int $maxDelayMilliseconds = 0)
{
if ($delayMilliseconds < 0) {
throw new InvalidArgumentException(sprintf('Delay must be greater than or equal to zero: "%s" passed.', $delayMilliseconds));
throw new InvalidArgumentException(sprintf('Delay must be greater than or equal to zero: "%s" given.', $delayMilliseconds));
}
$this->delayMilliseconds = $delayMilliseconds;

if ($multiplier < 1) {
throw new InvalidArgumentException(sprintf('Multiplier must be greater than zero: "%s" passed.', $multiplier));
throw new InvalidArgumentException(sprintf('Multiplier must be greater than zero: "%s" given.', $multiplier));
}
$this->multiplier = $multiplier;

if ($maxDelayMilliseconds < 0) {
throw new InvalidArgumentException(sprintf('Max delay must be greater than or equal to zero: "%s" passed.', $maxDelayMilliseconds));
throw new InvalidArgumentException(sprintf('Max delay must be greater than or equal to zero: "%s" given.', $maxDelayMilliseconds));
}
$this->maxDelayMilliseconds = $maxDelayMilliseconds;
}
Expand Down
Expand Up @@ -19,7 +19,7 @@
*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
final class HttpCodeDecider implements RetryDeciderInterface
final class HttpStatusCodeDecider implements RetryDeciderInterface
{
private $statusCodes;

Expand Down
Expand Up @@ -17,7 +17,7 @@
use Symfony\Component\HttpClient\Response\AsyncResponse;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Component\HttpClient\Retry\ExponentialBackOff;
use Symfony\Component\HttpClient\Retry\HttpCodeDecider;
use Symfony\Component\HttpClient\Retry\HttpStatusCodeDecider;
use Symfony\Component\HttpClient\Retry\RetryBackOffInterface;
use Symfony\Component\HttpClient\Retry\RetryDeciderInterface;
use Symfony\Contracts\HttpClient\ChunkInterface;
Expand All @@ -26,11 +26,11 @@
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
* Automatically retries the failing requests.
* Automatically retries failing HTTP requests.
*
* @author Jérémy Derussé <jeremy@derusse.com>
*/
class RetryHttpClient implements HttpClientInterface
class RetryableHttpClient implements HttpClientInterface
{
use AsyncDecoratorTrait;

Expand All @@ -45,7 +45,7 @@ class RetryHttpClient implements HttpClientInterface
public function __construct(HttpClientInterface $client, RetryDeciderInterface $decider = null, RetryBackOffInterface $strategy = null, int $maxRetries = 3, LoggerInterface $logger = null)
{
$this->client = $client;
$this->decider = $decider ?? new HttpCodeDecider();
$this->decider = $decider ?? new HttpStatusCodeDecider();
$this->strategy = $strategy ?? new ExponentialBackOff();
$this->maxRetries = $maxRetries;
$this->logger = $logger ?: new NullLogger();
Expand Down

0 comments on commit 923633a

Please sign in to comment.