maxRetries = $maxRetries; $this->retryInterval = $retryInterval; $this->maxRetryDelay = $maxRetryDelay; $this->maxRetryTime = $maxRetryTime; $this->exponentialBase = $exponentialBase; $this->jitterInterval = $jitterInterval; $this->options = $options; //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) { DefaultApi::log("ERROR", "Maximum retry attempts reached", $this->options); throw $e; } // throws exception when max retry time is exceeded if (microtime(true) * 1000 > $this->retryTimout) { DefaultApi::log("ERROR", "Maximum retry time $this->maxRetryTime ms exceeded", $this->options); 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."; DefaultApi::log("WARNING", $message, $this->options); usleep($timeout); $this->retry($callable, $attempts); } } public function isRetryable(ApiException $e): bool { $code = $e->getCode(); if (($code == null || $code < 429) && !($e->getPrevious() instanceof NetworkException)) { 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); } }