From 08e5077bc121b608b926a873161296fb1af35271 Mon Sep 17 00:00:00 2001 From: Robert Hajek Date: Tue, 18 May 2021 16:33:38 +0200 Subject: [PATCH 1/4] feat: exponential random retry --- README.md | 5 +- src/InfluxDB2/DefaultApi.php | 14 ---- src/InfluxDB2/WriteApi.php | 64 +++------------ src/InfluxDB2/WriteOptions.php | 21 +++-- src/InfluxDB2/WriteRetry.php | 140 +++++++++++++++++++++++++++++++++ tests/WriteApiBatchingTest.php | 20 ++--- tests/WriteApiTest.php | 102 ++++++++++++++++++++++-- 7 files changed, 276 insertions(+), 90 deletions(-) create mode 100644 src/InfluxDB2/WriteRetry.php diff --git a/README.md b/README.md index 5aa05a22..8e0ead44 100644 --- a/README.md +++ b/README.md @@ -166,8 +166,9 @@ The writes are processed in batches which are configurable by `WriteOptions`: | **retryInterval** | the number of milliseconds to retry unsuccessful write. The retry interval is "exponentially" used when the InfluxDB server does not specify "Retry-After" header. | 5000 | | **jitterInterval** | the number of milliseconds before the data is written increased by a random amount | 0 | | **maxRetries** | the number of max retries when write fails | 5 | -| **maxRetryDelay** | maximum delay when retrying write in milliseconds | 180000 | -| **exponentialBase** | the base for the exponential retry delay, the next delay is computed as `retryInterval * exponentialBase^(attempts-1)` | 5 | +| **maxRetryDelay** | maximum delay when retrying write in milliseconds | 125000 | +| **maxRetryTime** | maximum total retry timeout in milliseconds | 180000 | +| **exponentialBase** | the base for the exponential retry delay, the next delay is computed using random exponential backoff as a random value within the interval ``retryInterval * exponentialBase^(attempts-1)`` and ``retryInterval * exponentialBase^(attempts)``. Example for ``retryInterval=5000, exponentialBase=2, maxRetryDelay=125000, total=5`` Retry delays are random distributed values within the ranges of ``[5000-10000, 10000-20000, 20000-40000, 40000-80000, 80000-125000]`` | 2 | ```php use InfluxDB2\Client; use InfluxDB2\WriteType as WriteType; diff --git a/src/InfluxDB2/DefaultApi.php b/src/InfluxDB2/DefaultApi.php index 675b0f81..feb09c74 100644 --- a/src/InfluxDB2/DefaultApi.php +++ b/src/InfluxDB2/DefaultApi.php @@ -130,18 +130,4 @@ function ($v, $k) { throw new InvalidArgumentException("The '${key}' should be defined as argument or default option: {$options}"); } } - - /** - * Log message with specified severity to log file defined by: 'options['logFile']'. - * - * @param string $level log severity - * @param string $message log message - */ - protected function log(string $level, string $message): void - { - $logFile = isset($this->options['logFile']) ? $this->options['logFile'] : "php://output"; - $logDate = date('H:i:s d-M-Y'); - - file_put_contents($logFile, "[{$logDate}]: [{$level}] - {$message}", FILE_APPEND); - } } diff --git a/src/InfluxDB2/WriteApi.php b/src/InfluxDB2/WriteApi.php index e95f2108..ed645865 100644 --- a/src/InfluxDB2/WriteApi.php +++ b/src/InfluxDB2/WriteApi.php @@ -2,9 +2,6 @@ namespace InfluxDB2; -use GuzzleHttp\Exception\ConnectException; -use InfluxDB2\Model\WritePrecision; - /** * Write time series data into InfluxDB. * @package InfluxDB2 @@ -21,7 +18,7 @@ class WriteApi extends DefaultApi implements Writer /** * WriteApi constructor. * @param $options - * @param array $writeOptions + * @param array|null $writeOptions * @param array|null $pointSettings */ public function __construct($options, array $writeOptions = null, array $pointSettings = null) @@ -134,55 +131,20 @@ public function writeRaw(string $data, string $precision = null, string $bucket $queryParams = ["org" => $orgParam, "bucket" => $bucketParam, "precision" => $precisionParam]; - $this->writeRawInternal($data, $queryParams, 1, $this->writeOptions->retryInterval); - } - - private function writeRawInternal(string $data, array $queryParams, int $attempts, int $retryInterval) - { - if ($this->writeOptions->jitterInterval > 0) { - $jitterDelay = ($this->writeOptions->jitterInterval * 1000) * (rand(0, 1000) / 1000); - usleep($jitterDelay); - } - - try { + $retry = new WriteRetry( + $this->writeOptions->maxRetries, + $this->writeOptions->retryInterval, + $this->writeOptions->maxRetryDelay, + $this->writeOptions->exponentialBase, + $this->writeOptions->maxRetryTime, + $this->writeOptions->jitterInterval, + $this->options['logFile'] ?? "php://output" + ); + + $retry->retry(function () use ($data, $queryParams) { $this->post($data, "/api/v2/write", $queryParams); - } catch (ApiException $e) { - $code = $e->getCode(); - - if ($attempts > $this->writeOptions->maxRetries) { - throw $e; - } - - if (($code == null || $code < 429) && !($e->getPrevious() instanceof ConnectException)) { - throw $e; - } - - $headers = $e->getResponseHeaders(); - - if ($headers != null && array_key_exists('Retry-After', $headers)) { - $timeout = (int)$headers['Retry-After'][0] * 1000000.0; - } else { - $timeout = min($retryInterval, $this->writeOptions->maxRetryDelay) * 1000.0; - } - - $timeoutInSec = $timeout / 1000000.0; - $error = $e->getResponseBody(); - $error = isset($error) ? $error : $e->getMessage(); - - $message = "The retriable error occurred during writing of data. Reason: '{$error}'. Retry in: {$timeoutInSec}s."; - $this->log("WARNING", $message); - - usleep($timeout); - - $this->writeRawInternal( - $data, - $queryParams, - $attempts + 1, - $retryInterval * $this->writeOptions->exponentialBase - ); - } + }); } - public function close() { $this->closed = true; diff --git a/src/InfluxDB2/WriteOptions.php b/src/InfluxDB2/WriteOptions.php index 79a1baa9..0836f68a 100644 --- a/src/InfluxDB2/WriteOptions.php +++ b/src/InfluxDB2/WriteOptions.php @@ -7,8 +7,9 @@ class WriteOptions const DEFAULT_BATCH_SIZE = 10; const DEFAULT_RETRY_INTERVAL = 5000; const DEFAULT_MAX_RETRIES = 5; - const DEFAULT_MAX_RETRY_DELAY = 180000; - const DEFAULT_EXPONENTIAL_BASE = 5; + const DEFAULT_MAX_RETRY_DELAY = 125000; + const DEFAULT_MAX_RETRY_TIME = 180000; + const DEFAULT_EXPONENTIAL_BASE = 2; const DEFAULT_JITTER_INTERVAL = 0; public $writeType; @@ -18,6 +19,7 @@ class WriteOptions public $maxRetryDelay; public $exponentialBase; public $jitterInterval; + public $maxRetryTime; /** * WriteOptions constructor. @@ -27,12 +29,18 @@ class WriteOptions * 'retryInterval' => number of milliseconds to retry unsuccessful write * 'maxRetries' => max number of retries when write fails * The retry interval is used when the InfluxDB server does not specify "Retry-After" header. - * 'maxRetryDelay' => maximum delay when retrying write - * 'exponentialBase' => the base for the exponential retry delay, the next delay is computed as - * `retry_interval * exponentialBase^(attempts - 1)` + * 'maxRetryDelay' => maximum delay when retrying write in milliseconds + * 'maxRetryTime' => maximum total time when retrying write in milliseconds + * 'exponentialBase' => the base for the exponential retry delay, the next delay is computed using + * random exponential backoff as a random value within the interval + * ``retryInterval * exponentialBase^(attempts-1)`` and + * ``retryInterval * exponentialBase^(attempts)``. + * Example for ``retryInterval=5000, exponentialBase=2, maxRetryDelay=125000, total=5`` + * Retry delays are random distributed values within the ranges of + * ``[5000-10000, 10000-20000, 20000-40000, 40000-80000, 80000-125000]`` * 'jitterInterval' => the number of milliseconds before the data is written increased by a random amount * ] - * @param array $writeOptions Array containing the write parameters (See above) + * @param array|null $writeOptions Array containing the write parameters (See above) */ public function __construct(array $writeOptions = null) { @@ -42,6 +50,7 @@ public function __construct(array $writeOptions = null) $this->retryInterval = $writeOptions["retryInterval"] ?? self::DEFAULT_RETRY_INTERVAL; $this->maxRetries = $writeOptions["maxRetries"] ?? self::DEFAULT_MAX_RETRIES; $this->maxRetryDelay = $writeOptions["maxRetryDelay"] ?? self::DEFAULT_MAX_RETRY_DELAY; + $this->maxRetryTime = $writeOptions["maxRetryTime"] ?? self::DEFAULT_MAX_RETRY_TIME; $this->exponentialBase = $writeOptions["exponentialBase"] ?? self::DEFAULT_EXPONENTIAL_BASE; $this->jitterInterval = $writeOptions["jitterInterval"] ?? self::DEFAULT_JITTER_INTERVAL; } diff --git a/src/InfluxDB2/WriteRetry.php b/src/InfluxDB2/WriteRetry.php new file mode 100644 index 00000000..7295b78c --- /dev/null +++ b/src/InfluxDB2/WriteRetry.php @@ -0,0 +1,140 @@ +maxRetries = $maxRetries; + $this->retryInterval = $retryInterval; + $this->maxRetryDelay = $maxRetryDelay; + $this->maxRetryTime = $maxRetryTime; + $this->exponentialBase = $exponentialBase; + $this->jitterInterval = $jitterInterval; + $this->logFile = $logFile; + + //retry timout + $this->retryTimout = microtime(true) * 1000 + $maxRetryTime; + } + + /** + * @throws ApiException + */ + public function retry($callable, $attempts = 0) + { + try { + return call_user_func($callable); + } catch (ApiException $e) { + + $error = $e->getResponseBody() ?? $e->getMessage(); + + if (!$this->isRetryable($e)) { + throw $e; + } + $attempts++; + if ($attempts > $this->maxRetries || !$this->isRetryable($e)) { + $this->log("ERROR", "Maximum retry attempts reached"); + throw $e; + } + + // throws exception when max retry time is exceeded + if (microtime(true) * 1000 > $this->retryTimout) { + $this->log("ERROR", "Maximum retry time $this->maxRetryTime ms exceeded"); + throw $e; + } + + $headers = $e->getResponseHeaders(); + if ($headers != null && array_key_exists('Retry-After', $headers)) { + //jitter add in microseconds + $jitterMicro = rand(0, $this->jitterInterval) * 1000; + $timeout = (int)$headers['Retry-After'][0] * 1000000.0 + $jitterMicro; + } else { + $timeout = $this->getBackoffTime($attempts) * 1000; + } + + $timeoutInSec = $timeout / 1000000.0; + + $message = "The retryable error occurred during writing of data. Reason: '$error'. Retry in: {$timeoutInSec}s."; + $this->log("WARNING", $message); + usleep($timeout); + $this->retry($callable, $attempts); + } + } + + public function isRetryable(ApiException $e): bool + { + $code = $e->getCode(); + if (($code == null || $code < 429) && + !($e->getPrevious() instanceof ConnectException)) { + return false; + } + return true; + } + + public function getBackoffTime(int $attempt) + { + $range_start = $this->retryInterval; + $range_stop = $this->retryInterval * $this->exponentialBase; + + $i = 1; + while ($i < $attempt) { + $i += 1; + $range_start = $range_stop; + $range_stop = $range_stop * $this->exponentialBase; + if ($range_stop > $this->maxRetryDelay) + break; + } + + if ($range_stop > $this->maxRetryDelay) { + $range_stop = $this->maxRetryDelay; + } + return $range_start + ($range_stop - $range_start) * (rand(0, 1000) / 1000); + } + + private function log(string $level, string $message): void + { + $logDate = date('H:i:s d-M-Y'); + file_put_contents($this->logFile, "[$logDate]: [$level] - $message".PHP_EOL, FILE_APPEND); + } + +} diff --git a/tests/WriteApiBatchingTest.php b/tests/WriteApiBatchingTest.php index 050e8fa7..0b0de43c 100644 --- a/tests/WriteApiBatchingTest.php +++ b/tests/WriteApiBatchingTest.php @@ -32,7 +32,7 @@ public function testBatchSize() $this->writeApi->write('h2o_feet,location=coyote_creek level\\ water_level=3.0 3'); $this->writeApi->write('h2o_feet,location=coyote_creek level\\ water_level=4.0 4'); - $this->assertEquals(2, count($this->container)); + $this->assertCount(2, $this->container); $result1 = "h2o_feet,location=coyote_creek level\\ water_level=1.0 1\n" . "h2o_feet,location=coyote_creek level\\ water_level=2.0 2"; @@ -93,7 +93,7 @@ public function testBatchSizeGroupBy() 'my-org-a' ); - $this->assertEquals(5, count($this->container)); + $this->assertCount(5, $this->container); $request = $this->container[0]['request']; @@ -176,7 +176,7 @@ public function testRetryIntervalByConfig() $this->writeApi->write('h2o_feet,location=coyote_creek water_level=1.0 1'); $this->writeApi->write('h2o_feet,location=coyote_creek water_level=2.0 2'); - $this->assertEquals(2, count($this->container)); + $this->assertCount(2, $this->container); $request = $this->mockHandler->getLastRequest(); $this->assertEquals( @@ -202,7 +202,7 @@ public function testRetryIntervalByHeader() $this->writeApi->write('h2o_feet,location=coyote_creek water_level=1.0 1'); $this->writeApi->write('h2o_feet,location=coyote_creek water_level=2.0 2'); - $this->assertEquals(2, count($this->container)); + $this->assertCount(2, $this->container); $request = $this->mockHandler->getLastRequest(); $this->assertEquals( @@ -231,7 +231,7 @@ public function testRetryIntervalMaxRetries() $this->writeApi->write('h2o_feet,location=coyote_creek water_level=1.0 1'); $this->writeApi->write('h2o_feet,location=coyote_creek water_level=2.0 2'); - $this->assertEquals(3, count($this->container)); + $this->assertCount(3, $this->container); } public function testRetryCount() @@ -265,7 +265,7 @@ public function testRetryCount() $this->assertEquals(429, $e->getCode()); } - $this->assertEquals(4, count($this->container)); + $this->assertCount(4, $this->container); $count = $this->mockHandler->count(); $this->assertEquals(1, $count); @@ -291,7 +291,7 @@ public function testRetryConnectionError() $this->writeApi->write('h2o_feet,location=coyote_creek water_level=1.0 1'); $this->writeApi->write('h2o_feet,location=coyote_creek water_level=2.0 2'); - $this->assertEquals(3, count($this->container)); + $this->assertCount(3, $this->container); } public function testJitterInterval() @@ -312,7 +312,7 @@ public function testJitterInterval() $this->assertTrue($time > 0 && $time <= 2); - $this->assertEquals(1, count($this->container)); + $this->assertCount(1, $this->container); $result1 = "h2o_feet,location=coyote_creek level\\ water_level=1.0 1\n" . "h2o_feet,location=coyote_creek level\\ water_level=2.0 2"; @@ -341,9 +341,9 @@ public function testRetryContainsMessage() $this->writeApi->write('h2o_feet,location=coyote_creek water_level=1.0 1'); $this->writeApi->write('h2o_feet,location=coyote_creek water_level=2.0 2'); - $this->assertEquals(2, count($this->container)); + $this->assertCount(2, $this->container); $message = file_get_contents("log_test.txt"); - $this->assertStringContainsString("The retriable error occurred during writing of data. Reason: 'org 04014de4ed590000 has exceeded limited_write plan limit'. Retry in: 3s.", $message); + $this->assertStringContainsString("The retryable error occurred during writing of data. Reason: 'org 04014de4ed590000 has exceeded limited_write plan limit'. Retry in: 3s.", $message); } } diff --git a/tests/WriteApiTest.php b/tests/WriteApiTest.php index 9786881b..0abff4de 100644 --- a/tests/WriteApiTest.php +++ b/tests/WriteApiTest.php @@ -2,9 +2,14 @@ namespace InfluxDB2Test; +use Exception; +use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Psr7\Response; use InfluxDB2\ApiException; +use InfluxDB2\Client; +use InfluxDB2\Model\WritePrecision; use InfluxDB2\Point; +use InfluxDB2\WriteRetry; require_once('BasicTest.php'); @@ -142,7 +147,7 @@ public function testInfluxException() $this->assertEquals(400, $e->getCode()); $this->assertEquals('invalid', implode($e->getResponseHeaders()['X-Platform-Error-Code'])); $this->assertEquals($errorBody, strval($e->getResponseBody())); - } catch (\Exception $e) { + } catch (Exception $e) { $this->fail(); } } @@ -282,15 +287,98 @@ public function testRetryCount() ->addTag('location', 'europe') ->addField('level', 2); + $this->expectExceptionCode(429); + $this->expectException(ApiException::class); + $this->writeApi->write($point); + + $this->assertCount(4, $this->container); + $this->assertCount(1, $this->mockHandler); + } + + public function testRetryMaxTime() + { + $this->mockHandler->append( + // regular call + new Response(429), + // retry + new Response(429), + // retry + new Response(200) + ); + + $this->writeApi->writeOptions->retryInterval = 1000; + $this->writeApi->writeOptions->maxRetries = 3; + $this->writeApi->writeOptions->maxRetryDelay = 15000; + $this->writeApi->writeOptions->exponentialBase = 2; + $this->writeApi->writeOptions->maxRetryTime = 300; + + $point = Point::measurement('h2o') + ->addTag('location', 'europe') + ->addField('level', 2); + + $this->expectException(ApiException::class); + $this->expectExceptionCode(429); + + $this->writeApi->write($point); + + $this->assertCount(2, $this->container); + $this->assertCount(1, $this->mockHandler); + } + + + public function testRetryBackoffTime() { + + $retry = new WriteRetry(); + + $backoff = $retry->getBackoffTime(1); + $this->assertGreaterThan(5000, $backoff); + $this->assertLessThan(10000, $backoff); + + $backoff = $retry->getBackoffTime(2); + $this->assertGreaterThan(10000, $backoff); + $this->assertLessThan(20000, $backoff); + + $backoff = $retry->getBackoffTime(3); + $this->assertGreaterThan(20000, $backoff); + $this->assertLessThan(40000, $backoff); + + $backoff = $retry->getBackoffTime(4); + $this->assertGreaterThan(40000, $backoff); + $this->assertLessThan(80000, $backoff); + + $backoff = $retry->getBackoffTime(5); + $this->assertGreaterThan(80000, $backoff); + $this->assertLessThan(125000, $backoff); + + $backoff = $retry->getBackoffTime(6); + $this->assertGreaterThan(80000, $backoff); + $this->assertLessThan(125000, $backoff); + } + + public function testConnectExceptionRetry () { + $client = new Client([ + "url" => "http://nonexistenthost:8086/", + "token" => "my-token", + "bucket" => "my-bucket", + "precision" => WritePrecision::NS, + "org" => "my-org", + "logFile" => "php://output" + ]); + + $writeApi = $client->createWriteApi( ["retryInterval" => 100] ); + $point = Point::measurement('h2o') + ->addTag('location', 'europe') + ->addField('level', 2); + + $this->expectException(ApiException::class); + $this->expectExceptionMessage("Could not resolve host: nonexistenthost"); + try { - $this->writeApi->write($point); + $writeApi->write($point); } catch (ApiException $e) { - $this->assertEquals(429, $e->getCode()); + $this->assertEquals(ConnectException::class, get_class($e->getPrevious())); + throw $e; } - $this->assertEquals(4, count($this->container)); - - $count = $this->mockHandler->count(); - $this->assertEquals(1, $count); } } From e8ac07cb6df2d09c56418ccf61901bc3f0cb2548 Mon Sep 17 00:00:00 2001 From: Robert Hajek Date: Tue, 18 May 2021 17:14:03 +0200 Subject: [PATCH 2/4] feat: fix codding style --- .circleci/config.yml | 2 +- src/InfluxDB2/WriteRetry.php | 19 +++++++++++-------- tests/WriteApiTest.php | 12 ++++++------ 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 93b9e4f3..ffbe6ca1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -117,7 +117,7 @@ jobs: - checkout - run: | mkdir -p tools/php-cs-fixer - composer require --working-dir=tools/php-cs-fixer friendsofphp/php-cs-fixer + composer require --working-dir=tools/php-cs-fixer friendsofphp/php-cs-fixer:2.18.7 tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --dry-run --verbose --show-progress=estimating --using-cache=no --diff workflows: diff --git a/src/InfluxDB2/WriteRetry.php b/src/InfluxDB2/WriteRetry.php index 7295b78c..1d5a255f 100644 --- a/src/InfluxDB2/WriteRetry.php +++ b/src/InfluxDB2/WriteRetry.php @@ -40,11 +40,15 @@ class WriteRetry * @param int $jitterInterval the number of milliseconds before the data is written increased by a random amount * @param string $logFile logfile */ - public function __construct(int $maxRetries = 5, int $retryInterval = 5000 - , int $maxRetryDelay = 125000, int $exponentialBase = 2, - int $maxRetryTime = 180000, - int $jitterInterval = 0, string $logFile = "php://output") - { + public function __construct( + int $maxRetries = 5, + int $retryInterval = 5000, + int $maxRetryDelay = 125000, + int $exponentialBase = 2, + int $maxRetryTime = 180000, + int $jitterInterval = 0, + string $logFile = "php://output" + ) { $this->maxRetries = $maxRetries; $this->retryInterval = $retryInterval; $this->maxRetryDelay = $maxRetryDelay; @@ -65,7 +69,6 @@ public function retry($callable, $attempts = 0) try { return call_user_func($callable); } catch (ApiException $e) { - $error = $e->getResponseBody() ?? $e->getMessage(); if (!$this->isRetryable($e)) { @@ -121,8 +124,9 @@ public function getBackoffTime(int $attempt) $i += 1; $range_start = $range_stop; $range_stop = $range_stop * $this->exponentialBase; - if ($range_stop > $this->maxRetryDelay) + if ($range_stop > $this->maxRetryDelay) { break; + } } if ($range_stop > $this->maxRetryDelay) { @@ -136,5 +140,4 @@ private function log(string $level, string $message): void $logDate = date('H:i:s d-M-Y'); file_put_contents($this->logFile, "[$logDate]: [$level] - $message".PHP_EOL, FILE_APPEND); } - } diff --git a/tests/WriteApiTest.php b/tests/WriteApiTest.php index 0abff4de..d12c5598 100644 --- a/tests/WriteApiTest.php +++ b/tests/WriteApiTest.php @@ -326,8 +326,8 @@ public function testRetryMaxTime() } - public function testRetryBackoffTime() { - + public function testRetryBackoffTime() + { $retry = new WriteRetry(); $backoff = $retry->getBackoffTime(1); @@ -355,8 +355,9 @@ public function testRetryBackoffTime() { $this->assertLessThan(125000, $backoff); } - public function testConnectExceptionRetry () { - $client = new Client([ + public function testConnectExceptionRetry() + { + $client = new Client([ "url" => "http://nonexistenthost:8086/", "token" => "my-token", "bucket" => "my-bucket", @@ -365,7 +366,7 @@ public function testConnectExceptionRetry () { "logFile" => "php://output" ]); - $writeApi = $client->createWriteApi( ["retryInterval" => 100] ); + $writeApi = $client->createWriteApi(["retryInterval" => 100]); $point = Point::measurement('h2o') ->addTag('location', 'europe') ->addField('level', 2); @@ -379,6 +380,5 @@ public function testConnectExceptionRetry () { $this->assertEquals(ConnectException::class, get_class($e->getPrevious())); throw $e; } - } } From 3e67a552d83e069292f756889b4e3770d2a0b91b Mon Sep 17 00:00:00 2001 From: Robert Hajek Date: Wed, 19 May 2021 09:59:30 +0200 Subject: [PATCH 3/4] feat: code cleanup --- src/InfluxDB2/WriteRetry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InfluxDB2/WriteRetry.php b/src/InfluxDB2/WriteRetry.php index 1d5a255f..06997bc1 100644 --- a/src/InfluxDB2/WriteRetry.php +++ b/src/InfluxDB2/WriteRetry.php @@ -75,7 +75,7 @@ public function retry($callable, $attempts = 0) throw $e; } $attempts++; - if ($attempts > $this->maxRetries || !$this->isRetryable($e)) { + if ($attempts > $this->maxRetries) { $this->log("ERROR", "Maximum retry attempts reached"); throw $e; } From fdbb568b589527f535068bc787fa2b13904d6bed Mon Sep 17 00:00:00 2001 From: Robert Hajek Date: Wed, 19 May 2021 10:03:15 +0200 Subject: [PATCH 4/4] feat: code cleanup --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c968cca4..66b557c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## 1.13.0 [unreleased] +### Features +1. [#76](https://github.com/influxdata/influxdb-client-php/pull/76): Exponential random backoff retry strategy + ## 1.12.0 [2021-04-01] ### Features pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy