Skip to content
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

Add retry logic to default http client #455

Merged
merged 5 commits into from
Sep 27, 2023
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
44 changes: 44 additions & 0 deletions src/MercadoPago/MercadoPagoConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ class MercadoPagoConfig
/** @var string access token */
private static string $access_token = "";

/** @var int max retries */
private static int $max_retries = 3;

/** @var int retry delay (in milliseconds) */
private static int $retry_delay = 500;

/** @var int max connections */
private static int $max_connections = 10;

Expand Down Expand Up @@ -69,6 +75,44 @@ public static function setAccessToken(string $access_token): void
self::$access_token = $access_token;
}

/**
* Gets max retries.
* @return int max retries
*/
public static function getMaxRetries(): int
{
return self::$max_retries;
}

/**
* Sets max retries.
* @param int $max_retries max retries
* @return void max retries
*/
public static function setMaxRetries(int $max_retries): void
{
self::$max_retries = $max_retries;
}

/**
* Gets retry delay.
* @return int retry delay
*/
public static function getRetryDelay(): int
{
return self::$retry_delay;
}

/**
* Sets retry delay.
* @param int $retry_delay retry delay
* @return void retry delay
*/
public static function setRetryDelay(int $retry_delay): void
{
self::$retry_delay = $retry_delay;
}

/**
* Gets max connections.
* @return int max connections
Expand Down
52 changes: 51 additions & 1 deletion src/MercadoPago/Net/MPDefaultHttpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
/** Mercado Pago default Http Client class. */
class MPDefaultHttpClient implements MPHttpClient
{
private const ONE_MILLISECOND = 1000;

private HttpRequest $httpRequest;

/**
Expand All @@ -28,6 +30,32 @@ public function __construct(?HttpRequest $httpRequest = null)
* @return \MercadoPago\Net\MPResponse response from the request.
*/
public function send(MPRequest $request): MPResponse
{
$max_retries = MercadoPagoConfig::getMaxRetries();

for ($retry_count = 0; $retry_count <= $max_retries; $retry_count++) {
try {
return $this->makeRequest($request);
} catch (MPApiException $e) {
$status_code = $e->getApiResponse()->getStatusCode();
if ($this->isServerError($status_code) && !$this->isLastRetry($retry_count)) {
$this->doExponentialBackoff($retry_count);
} else {
throw $e;
}
} catch (Exception $e) {
if (!$this->isLastRetry($retry_count)) {
$this->doExponentialBackoff($retry_count);
} else {
throw $e;
}
}
}

throw new Exception("Error processing request. Please try again.");
}

private function makeRequest(MPRequest $request): MPResponse
{
$request_options = $this->createHttpRequestOptions($request);
$this->httpRequest->setOptionArray($request_options);
Expand All @@ -41,7 +69,7 @@ public function send(MPRequest $request): MPResponse
$this->httpRequest->close();
throw new Exception($error_message);
}
if ($status_code < 200 || $status_code >= 300) {
if ($this->isApiError($status_code)) {
$this->httpRequest->close();
throw new MPApiException("Api error. Check response for details", $mp_response);
}
Expand All @@ -50,6 +78,13 @@ public function send(MPRequest $request): MPResponse
return $mp_response;
}

private function doExponentialBackoff(int $retry_count): void
{
$exponential_backoff_time = pow(2, $retry_count);
$retry_delay_microseconds = $exponential_backoff_time * self::ONE_MILLISECOND * MercadoPagoConfig::getRetryDelay();
usleep($retry_delay_microseconds);
}

private function createHttpRequestOptions(MPRequest $request): array
{
$connection_timeout = $request->getConnectionTimeout() ?: MercadoPagoConfig::getConnectionTimeout();
Expand All @@ -64,4 +99,19 @@ private function createHttpRequestOptions(MPRequest $request): array
CURLOPT_RETURNTRANSFER => true
);
}

private function isServerError(int $status_code): bool
{
return $status_code >= 500;
}

private function isApiError(int $status_code): bool
{
return $status_code < 200 || $status_code >= 300;
}

private function isLastRetry(int $retry_count): bool
{
return $retry_count >= MercadoPagoConfig::getMaxRetries();
}
}
Loading