Repository navigation
Expand file tree
/
Copy pathExponentialBackoffRetryPolicy.php
More file actions
99 lines (92 loc) · 3.23 KB
/
Copy pathExponentialBackoffRetryPolicy.php
File metadata and controls
99 lines (92 loc) · 3.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Apache\Rocketmq;
/**
* ExponentialBackoffRetryPolicy - Retry policy with exponential backoff.
*
* Computes the next retry delay based on the current attempt number with
* the formula:
* delay = min(base * multiplier^(attempt-1), maxDelay)
*
* With jitter to avoid thundering herd:
* delayWithJitter = delay * (0.5 + random * 0.5)
*/
class ExponentialBackoffRetryPolicy
{
/**
* Construct a new ExponentialBackoffRetryPolicy.
*
* @param int $maxAttempts Maximum retry attempts (>= 1)
* @param int $baseDelayMs Base delay in milliseconds
* @param int $maxDelayMs Maximum delay cap in milliseconds
* @param float $multiplier Multiplier for each subsequent attempt
* @throws \InvalidArgumentException if maxAttempts < 1
*/
public function __construct(
protected readonly int $maxAttempts = 3,
private int $baseDelayMs = 1000,
private int $maxDelayMs = 30000,
private float $multiplier = 2.0,
) {
if ($this->maxAttempts < 1) {
throw new \InvalidArgumentException("maxAttempts must be >= 1");
}
$this->baseDelayMs = max(0, $baseDelayMs);
$this->maxDelayMs = max($this->baseDelayMs, $maxDelayMs);
$this->multiplier = max(1.0, $multiplier);
}
/**
* Get the maximum number of attempts.
*
* @return int
*/
public function getMaxAttempts(): int
{
return $this->maxAttempts;
}
/**
* Compute the next delay in milliseconds for the given attempt.
*
* @param int $attempt Current attempt number (1-based)
* @return int Delay in milliseconds
*/
public function getNextDelayMs(int $attempt): int
{
if ($attempt >= $this->maxAttempts) {
return 0;
}
$delay = $this->baseDelayMs * pow($this->multiplier, $attempt - 1);
return min((int)$delay, $this->maxDelayMs);
}
/**
* Compute the next delay with jitter to avoid thundering herd.
* Jitter adds random factor: delay * (0.5 + rand(0,1) * 0.5)
*
* @param int $attempt Current attempt number (1-based)
* @return int Delay in milliseconds with jitter
*/
public function getNextDelayWithJitterMs(int $attempt): int
{
$delay = $this->getNextDelayMs($attempt);
if ($delay <= 0) {
return 0;
}
$jitter = 0.5 + (mt_rand() / mt_getrandmax()) * 0.5;
return (int)($delay * $jitter);
}
}