VOOZH about

URL: https://deepwiki.com/hypervel/http-client/10.3-retry-logic-and-error-recovery

⇱ Retry Logic and Error Recovery | hypervel/http-client | DeepWiki


Loading...
Menu

Retry Logic and Error Recovery

This page documents the automatic retry mechanism in the Hypervel HTTP Client, including retry configuration, delay strategies, conditional retry logic, and integration with error handling.

Related documentation: Error Handling and Exceptions, Event System.


Overview

The PendingRequest class implements built-in retry logic for handling transient failures, network issues, and unsuccessful HTTP responses. Retry behavior applies to both synchronous and asynchronous requests.

Core Capabilities:

  • Configurable retry attempts with static or dynamic delays
  • Conditional retry via callback functions
  • Exponential backoff and per-attempt delay arrays
  • Exception throwing control after retry exhaustion
  • Separate handling paths for connection failures vs HTTP errors
  • Integration with the retry() helper function (synchronous) and promise chains (asynchronous)

Sources: src/PendingRequest.php106-125 src/PendingRequest.php496-508

Retry Configuration Properties

The PendingRequest class maintains four core properties that control retry behavior:

PropertyTypeDefaultPurpose
$triesint|array1Number of retry attempts (array indicates per-attempt delays)
$retryDelayint|Closure100Milliseconds to wait between retries, or callback for dynamic delays
$retryThrowbooltrueWhether to throw exception when all retries fail
$retryWhenCallbackcallable|nullnullCallback to determine if specific failure should be retried

Sources: src/PendingRequest.php106-125


The retry() Method

The retry() method configures all retry parameters in a single fluent call:


Parameters:

ParameterTypeDescription
$timesint|arrayTotal attempts (int) or delay array for each retry
$sleepMillisecondsint|ClosureStatic delay (ms) or callback fn($attempt, $exception)
$whencallable|nullCallback fn($exception, $request): bool for conditional retry
$throwboolThrow exception after all retries exhausted

Usage Examples:


Sources: src/PendingRequest.php496-508


Retry Delay Strategies

Static Delay

A static integer delay applies the same millisecond wait between all retry attempts:


Dynamic Delay with Closure

A Closure allows computing delays based on attempt number and the exception/response:


The Closure receives:

  • $attempt (int): Current attempt number (1-indexed)
  • $exception (Exception|Response): The failure that triggered the retry

Per-Attempt Delay Array

When $tries is an array, each element specifies the delay after that attempt:


This results in 4 total attempts (initial + 3 retries) with the specified delays between them.

Sources: src/PendingRequest.php113 src/PendingRequest.php759-761 src/PendingRequest.php917-926


Retry Decision Logic

Diagram: Synchronous Retry Decision Flow


This flowchart maps the retry decision logic in src/PendingRequest.php731-773 showing the interaction between $retryWhenCallback, $throwCallback, $throwIfCallback, and $retryThrow.

Sources: src/PendingRequest.php731-793


Conditional Retry: retryWhenCallback

The $retryWhenCallback property (set via the $when parameter in retry()) allows selective retry based on the failure type.

Callback Signature:


Invocation Points:

ContextException TypeCode Location
Unsuccessful response (sync)RequestException from response->toException()src/PendingRequest.php742-746
Retry decision (sync)Exception caught by retry() helpersrc/PendingRequest.php784-788
Unsuccessful response (async)RequestException from response->toException()src/PendingRequest.php908-912

Default Behavior: When $retryWhenCallback is null, the callback returns true, retrying all failures.

Example: Retry Only Transient Errors


Example: Retry Specific Status Codes


Sources: src/PendingRequest.php125 src/PendingRequest.php742-746 src/PendingRequest.php784-788 src/PendingRequest.php908-912


Synchronous Retry Implementation

Diagram: Synchronous Retry Sequence


The synchronous retry mechanism wraps request execution in the retry() helper function (from the support library), which:

  • Executes the closure up to $tries times
  • Catches exceptions thrown by response->throw() or connection failures
  • Applies $retryDelay between attempts
  • Calls $retryWhenCallback to determine retry eligibility
  • Re-throws the exception if all retries are exhausted

Sources: src/PendingRequest.php731-793


Asynchronous Retry Implementation

Asynchronous requests use promise chains instead of the retry() helper, implementing retry through recursive makePromise() calls.

Diagram: Asynchronous Retry Promise Chain


Key Differences from Synchronous Retry:

  • Uses promise .then() and .otherwise() chains instead of try/catch blocks
  • Retries via recursive makePromise() calls with incremented $attempt parameter
  • Delay applied by setting options['delay'] for the next promise
  • Exceptions are returned as promise values rather than thrown
  • handlePromiseResponse() method contains the retry decision logic

Sources: src/PendingRequest.php853-946


Retry Integration with Exception Handling

Connection Exception Handling

Connection failures (ConnectException from Guzzle) are caught in the send() method and wrapped in ConnectionException:


Connection failures are always eligible for retry unless $retryWhenCallback returns false. The retry() helper function catches the thrown ConnectionException and evaluates whether to retry based on the callback.

Sources: src/PendingRequest.php773-782

HTTP Error Response Handling

HTTP errors (4xx, 5xx) return Response objects with successful() = false. Retry logic:

  1. Calls $retryWhenCallback with $response->toException()
  2. Stores result in $shouldRetry
  3. Checks $throwCallback and $throwIfCallback conditions
  4. If conditions met and $shouldRetry = true, throws exception to trigger retry
  5. If all retries exhausted and $retryThrow = true, throws final exception

Sources: src/PendingRequest.php740-770

throwCallback and throwIfCallback Interaction

The $throwCallback and $throwIfCallback properties control exception throwing independently of retry logic:

PropertyTypePurposeCode Reference
$throwCallbackClosure|nullCallback executed when throwing exceptionsrc/PendingRequest.php98
$throwIfCallbackClosure|nullConditional check determining if exception should be thrownsrc/PendingRequest.php103

Evaluation Order:

  1. Response fails successful() check
  2. Call $retryWhenCallback → set $shouldRetry
  3. Check $throwIfCallback condition → if true, throw immediately (bypasses retry logic)
  4. Check retry conditions → throw to trigger retry if eligible
  5. Check $retryThrow → throw final exception after all retries exhausted

Example: Throw Immediately on Client Errors (No Retry)


Sources: src/PendingRequest.php97-103 src/PendingRequest.php753-757 src/PendingRequest.php932-939


Retry Count Calculation

The $potentialTries value determines total attempts (initial request + retries):































$tries ValueCalculationTotal AttemptsRetries
3 (int)$tries32
[100, 500] (array)count($tries) + 132
[100, 500, 1000, 2000] (array)count($tries) + 154

Integer $tries: Total attempts including initial request.

Array $tries: Array length = number of retries, each element = delay after that retry attempt.

Code References:

Sources: src/PendingRequest.php759-761 src/PendingRequest.php917-919


Event Dispatching During Retry

The event system dispatches events at key points during retry execution:

Event ClassDispatched ByWhenProperties
RequestSendingdispatchRequestSendingEvent()Before each attempt (including retries)Request $request
ResponseReceiveddispatchResponseReceivedEvent()After receiving response (any attempt)Request $request, Response $response
ConnectionFaileddispatchConnectionFailedEvent()On ConnectException (each attempt)Request $request, ConnectionException $exception

Event Dispatcher Integration:

Events are dispatched only if $factory->getDispatcher() returns a dispatcher instance. The RequestSending event fires from the beforeSendingCallbacks collection on every attempt.

Monitoring Retry Behavior:


Sources: src/PendingRequest.php1254-1282 src/PendingRequest.php738 src/PendingRequest.php779 src/PendingRequest.php859 src/PendingRequest.php866


Code Entities Reference

Key Classes and Methods

Exception Types

  • GuzzleHttp\Exception\ConnectException: Network-level connection failures
  • Hypervel\HttpClient\ConnectionException: Wrapped connection failures
  • RequestException: HTTP error responses (when throwing is enabled)

Sources: src/PendingRequest.php12 src/PendingRequest.php13 src/PendingRequest.php24


Best Practices

  1. Use conditional retries for transient errors: Only retry 5xx errors or connection failures, not 4xx client errors
  2. Implement exponential backoff: Use a Closure delay strategy to avoid overwhelming failing servers
  3. Set reasonable retry limits: Balance reliability with response time requirements (typically 2-3 retries)
  4. Log retry attempts: Use event listeners to track retry behavior in production
  5. Handle exhausted retries: Set throw: false when graceful degradation is preferred over exceptions

Sources: src/PendingRequest.php496-508 src/PendingRequest.php731-793