-
-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathRetryMiddleware.php
98 lines (84 loc) · 3.01 KB
/
RetryMiddleware.php
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
<?php
/**
* This file is part of the tarantool/client package.
*
* (c) Eugene Leonovich <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Tarantool\Client\Middleware;
use Tarantool\Client\Exception\ClientException;
use Tarantool\Client\Exception\CommunicationFailed;
use Tarantool\Client\Exception\ConnectionFailed;
use Tarantool\Client\Exception\UnexpectedResponse;
use Tarantool\Client\Handler\Handler;
use Tarantool\Client\Request\Request;
use Tarantool\Client\Response;
final class RetryMiddleware implements Middleware
{
private const DEFAULT_MAX_RETRIES = 2;
private const MAX_RETRIES_LIMIT = 10;
private const MAX_DELAY_MS = 60000;
/** @var \Closure */
private $getDelayMs;
/**
* @param \Closure $getDelayMs
*/
private function __construct($getDelayMs)
{
$this->getDelayMs = $getDelayMs;
}
public static function constant(int $maxRetries = self::DEFAULT_MAX_RETRIES, int $intervalMs = 100) : self
{
return new self(static function (int $retries) use ($maxRetries, $intervalMs) {
return $retries > $maxRetries ? null : $intervalMs;
});
}
public static function exponential(int $maxRetries = self::DEFAULT_MAX_RETRIES, int $baseMs = 10) : self
{
return new self(static function (int $retries) use ($maxRetries, $baseMs) {
return $retries > $maxRetries ? null : $baseMs ** $retries;
});
}
public static function linear(int $maxRetries = self::DEFAULT_MAX_RETRIES, int $differenceMs = 100) : self
{
return new self(static function (int $retries) use ($maxRetries, $differenceMs) {
return $retries > $maxRetries ? null : $differenceMs * $retries;
});
}
public static function custom(\Closure $getDelayMs) : self
{
return new self(static function (int $retries, \Throwable $e) use ($getDelayMs) : ?int {
return $getDelayMs($retries, $e);
});
}
public function process(Request $request, Handler $handler) : Response
{
$retries = 0;
do {
try {
return $handler->handle($request);
} catch (UnexpectedResponse $e) {
$handler->getConnection()->close();
break;
} catch (ConnectionFailed | CommunicationFailed $e) {
$handler->getConnection()->close();
goto retry;
} catch (ClientException $e) {
retry:
if (self::MAX_RETRIES_LIMIT === $retries) {
break;
}
if (null === $delayMs = ($this->getDelayMs)(++$retries, $e)) {
break;
}
$delayMs = \min($delayMs, self::MAX_DELAY_MS) / 2;
$delayMs += \mt_rand(0, $delayMs);
\usleep($delayMs * 1000);
}
} while (true);
throw $e;
}
}