Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Messenger] Add jitter parameter to MultiplierRetryStrategy #53328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1592,6 +1592,7 @@ function ($a) {
->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()
->floatNode('jitter')->defaultValue(0.1)->min(0)->max(1)->info('Randomness to apply to the delay (between 0 and 1)')->end()
->end()
->end()
->scalarNode('rate_limiter')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2195,7 +2195,8 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder
->replaceArgument(0, $transport['retry_strategy']['max_retries'])
->replaceArgument(1, $transport['retry_strategy']['delay'])
->replaceArgument(2, $transport['retry_strategy']['multiplier'])
->replaceArgument(3, $transport['retry_strategy']['max_delay']);
->replaceArgument(3, $transport['retry_strategy']['max_delay'])
->replaceArgument(4, $transport['retry_strategy']['jitter']);
$container->setDefinition($retryServiceId, $retryDefinition);

$transportRetryReferences[$name] = new Reference($retryServiceId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
abstract_arg('delay ms'),
abstract_arg('multiplier'),
abstract_arg('max delay ms'),
abstract_arg('jitter'),
])

// rate limiter
Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/Messenger/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ CHANGELOG
* Add option `redis_sentinel` as an alias for `sentinel_master`
* Add `--all` option to the `messenger:consume` command
* Make `#[AsMessageHandler]` final
* Add parameter `$jitter` to `MultiplierRetryStrategy` in order to randomize delay and prevent the thundering herd effect

7.0
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,16 @@ class MultiplierRetryStrategy implements RetryStrategyInterface
private int $delayMilliseconds;
private float $multiplier;
private int $maxDelayMilliseconds;
private float $jitter;

/**
* @param int $maxRetries The maximum number of times to retry
* @param int $delayMilliseconds Amount of time to delay (or the initial value when multiplier is used)
* @param float $multiplier Multiplier to apply to the delay each time a retry occurs
* @param int $maxDelayMilliseconds Maximum delay to allow (0 means no maximum)
* @param float $jitter Randomness to apply to the delay (between 0 and 1)
*/
public function __construct(int $maxRetries = 3, int $delayMilliseconds = 1000, float $multiplier = 1, int $maxDelayMilliseconds = 0)
public function __construct(int $maxRetries = 3, int $delayMilliseconds = 1000, float $multiplier = 1, int $maxDelayMilliseconds = 0, float $jitter = 0.1)
{
$this->maxRetries = $maxRetries;

Expand All @@ -61,6 +63,11 @@ public function __construct(int $maxRetries = 3, int $delayMilliseconds = 1000,
throw new InvalidArgumentException(sprintf('Max delay must be greater than or equal to zero: "%s" given.', $maxDelayMilliseconds));
}
$this->maxDelayMilliseconds = $maxDelayMilliseconds;

if ($jitter < 0 || $jitter > 1) {
throw new InvalidArgumentException(sprintf('Jitter must be between 0 and 1: "%s" given.', $jitter));
}
$this->jitter = $jitter;
}

/**
Expand All @@ -82,6 +89,11 @@ public function getWaitingTime(Envelope $message, \Throwable $throwable = null):

$delay = $this->delayMilliseconds * $this->multiplier ** $retries;

if ($this->jitter > 0) {
$randomness = (int) ($delay * $this->jitter);
$delay += random_int(-$randomness, +$randomness);
}

if ($delay > $this->maxDelayMilliseconds && 0 !== $this->maxDelayMilliseconds) {
return $this->maxDelayMilliseconds;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function testIsRetryableWithNoStamp()
*/
public function testGetWaitTime(int $delay, float $multiplier, int $maxDelay, int $previousRetries, int $expectedDelay)
{
$strategy = new MultiplierRetryStrategy(10, $delay, $multiplier, $maxDelay);
$strategy = new MultiplierRetryStrategy(10, $delay, $multiplier, $maxDelay, 0);
$envelope = new Envelope(new \stdClass(), [new RedeliveryStamp($previousRetries)]);

$this->assertSame($expectedDelay, $strategy->getWaitingTime($envelope));
Expand Down Expand Up @@ -89,4 +89,39 @@ public static function getWaitTimeTests(): iterable
yield [1000, 1.5555, 5000, 1, 1556];
yield [1000, 1.5555, 5000, 2, 2420];
}

/**
* @dataProvider getJitterTest
*/
public function testJitter(float $jitter, int $maxMin, int $maxMax)
{
$strategy = new MultiplierRetryStrategy(3, 1000, 1, 0, $jitter);
$envelope = new Envelope(new \stdClass());

$min = 1000;
$max = 1000;
for ($i = 0; $i < 100; ++$i) {
$delay = $strategy->getWaitingTime($envelope);
$min = min($min, $delay);
$max = max($max, $delay);
}

$this->assertGreaterThanOrEqual($maxMin, $min);
$this->assertLessThanOrEqual($maxMax, $max);
}

public static function getJitterTest(): iterable
{
yield [1.0, 0, 2000];
yield [0.9, 100, 1900];
yield [0.8, 200, 1800];
yield [0.7, 300, 1700];
yield [0.6, 400, 1600];
yield [0.5, 500, 1500];
yield [0.4, 600, 1400];
yield [0.3, 700, 1300];
yield [0.2, 800, 1200];
yield [0.1, 900, 1100];
yield [0.0, 1000, 1000];
}
}