Download the PHP package gregpriday/laravel-retry without Composer
On this page you can find all versions of the php package gregpriday/laravel-retry. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download gregpriday/laravel-retry
More information about gregpriday/laravel-retry
Files in gregpriday/laravel-retry
Package laravel-retry
Short Description A flexible retry mechanism for Laravel applications
License MIT
Informations about the package laravel-retry
Laravel Retry
A powerful, flexible, and deeply integrated retry system for Laravel applications that goes beyond simple retry loops. This package provides sophisticated retry strategies, deep Laravel integration, and comprehensive observability to make your applications more resilient to transient failures.
Introduction
In modern web applications, dealing with external services, APIs, and databases is commonplace. However, these interactions can fail due to temporary issues like network glitches, rate limits, or service unavailability. Laravel Retry provides a robust solution to handle these transient failures elegantly and efficiently.
What sets Laravel Retry apart:
- Comprehensive Retry Strategies: Beyond basic exponential backoff, offering sophisticated strategies like Circuit Breaker, Rate Limiting, AWS-style Decorrelated Jitter, Fibonacci, Response Content inspection, Total Timeout enforcement, and more.
- Deep Laravel Integration: Extends Laravel's HTTP Client with retry-focused macros (like
robustRetry
,withCircuitBreaker
) and enhances Pipelines to support per-stage retry configuration, all while leveraging Laravel's configuration and event systems. - Smart Exception Handling: Automatically detects and handles retryable exceptions with a flexible, extensible system.
- Rich Observability: Detailed context tracking and event system for monitoring and debugging retry sequences.
- Promise-like API: Clean, chainable interface for handling retry results without nested try/catch blocks.
Key Features
- Multiple built-in retry strategies for different scenarios
- Simple strategy alias system for easier configuration (e.g., 'exponential-backoff', 'circuit-breaker')
- Seamless integration with Laravel's HTTP Client through dedicated macros
- Per-pipe retry configuration in Laravel pipelines
- Automatic exception handler discovery
- Comprehensive retry context and event system
- Promise-like result handling
- Configurable through standard Laravel configuration
Real-World Problem Solving
Laravel Retry excels at solving common but tricky scenarios:
- APIs that return success status codes (e.g., 200 OK) but signal errors in the response body
- Operations requiring hard deadlines across multiple retry attempts
- Complex multi-step workflows needing different retry strategies per step
- Integration of sophisticated retry logic into HTTP calls without boilerplate
- Debugging and monitoring of complex retry sequences
- Easy extension for handling custom or third-party exceptions
Whether you're building a robust API client, managing complex workflows, or just want to make your application more resilient, Laravel Retry provides the tools and flexibility you need.
Table of Contents
- Installation
- Requirements
- Quick Start
- Configuration
- Basic Usage
- Simple Retry Operation
- The RetryResult Object
- Configuring Retry Operations
- Understanding Delay Configuration
- HTTP Client Integration
- Pipeline Integration
- Advanced Configuration
- Retry Strategies
- Combining Strategies
- Exception Handling
- Events & Monitoring
- Troubleshooting
- Contributing
- License
Installation
Requirements
- PHP 8.1 or higher
- Laravel 10.0, 11.0, or 12.0
Quick Start
-
Install the package via Composer:
- The package uses Laravel's auto-discovery, so the service provider and facade will be automatically registered. If you have disabled auto-discovery, manually add the following to your
config/app.php
:
Configuration
- Publish the configuration file:
This will create a config/retry.php
file in your application's configuration directory, where you can customize retry settings.
Configuration Summary
After publishing, you can customize the package's behavior in config/retry.php
. Here are the key options:
max_retries
(Env:RETRY_MAX_ATTEMPTS
): Maximum number of times an operation will be retried after the initial attempt fails (Default:3
).timeout
(Env:RETRY_TIMEOUT
): Maximum execution time per attempt in seconds (Default:120
).total_timeout
(Env:RETRY_TOTAL_TIMEOUT
): Maximum total time allowed for the entire operation, including all retries and delays (Default:300
).default
(Env:RETRY_STRATEGY
): The kebab-case alias of the default retry strategy (Default:exponential-backoff
).- Settings for each strategy are defined in the
strategies
section of the config file.
- Settings for each strategy are defined in the
Available built-in strategy aliases:
exponential-backoff
: Increases delay exponentially with each retry (Default)linear-backoff
: Increases delay by a fixed amount with each retryfixed-delay
: Uses the same delay for all retriesfibonacci-backoff
: Increases delay according to the Fibonacci sequencedecorrelated-jitter
: Uses AWS-style decorrelated jitter algorithmcircuit-breaker
: Stops retrying after a threshold of failuresrate-limit
: Controls retry frequency with Laravel's Rate Limitertotal-timeout
: Enforces a maximum total time across all retry attemptsguzzle-response
: Intelligently handles HTTP retries based on response headersresponse-content
: Inspects HTTP response bodies for error conditionscustom-options
: Allows for flexible, customized retry behaviorcallback-retry
: Enables completely custom retry logic via callbacks
strategies
: Defines the default constructor options for each strategy when invoked via its alias (used when a strategy is specified as thedefault
or as an inner strategy).- Dedicated sections for
circuit_breaker
andresponse_content
provide more detailed configuration options for those specific strategies.
- Dedicated sections for
dispatch_events
(Env:RETRY_DISPATCH_EVENTS
): Enables/disables Laravel events during the retry lifecycle for monitoring (Default:true
).handler_paths
: Directories containing customRetryableExceptionHandler
classes for automatic discovery.
For full configuration details, refer to the published config/retry.php
file.
- (Optional) Publish exception handlers:
This will copy the built-in exception handlers to your application's app/Exceptions/Retry/Handlers
directory, allowing you to customize them or use them as templates for your own handlers.
Get Started in 2 Minutes
Here's a minimal example to get started with Laravel Retry:
That's it! Laravel Retry will automatically handle common HTTP exceptions and retry with exponential backoff.
Basic Usage
Simple Retry Operation
The most basic way to use Laravel Retry is through the Retry
facade:
You can customize retry behavior using a fluent interface:
The RetryResult Object
The Retry::run()
method returns a RetryResult
object that provides a promise-like interface for handling the operation's outcome. This avoids nested try/catch blocks and makes your code more readable.
Available methods on the RetryResult
object:
Method | Description |
---|---|
then(Closure $callback) |
Executes the callback if the operation succeeds, passing the operation's result as its parameter. Returns a new RetryResult with the callback's return value. |
catch(Closure $callback) |
Executes the callback if the operation fails, passing the exception as its parameter. Returns a new RetryResult with the callback's return value. |
finally(Closure $callback) |
Executes the callback regardless of whether the operation succeeds or fails. The callback receives no parameters. Returns the original RetryResult . |
value() |
Returns the operation's result directly. If the operation failed, throws the last exception that was caught. |
throw() |
Same as value() but with a more explicit name when you expect an exception may be thrown. |
throwFirst() |
Returns the result directly, but if the operation failed, throws the first exception that was caught instead of the last one. |
Example with the finally
method:
Example with comprehensive error handling:
Configuring Retry Operations
Laravel Retry provides several configuration options through fluent methods that allow you to customize how retry operations behave. These methods override the global configuration defined in config/retry.php
for a specific operation:
Available Configuration Methods
Fluent Method | Description | Config Key |
---|---|---|
maxRetries(int $retries) |
Sets maximum number of retry attempts after the initial try. | max_retries |
timeout(int $seconds) |
Sets maximum execution time per attempt in seconds. | timeout |
withStrategy(RetryStrategy $strategy) |
Specifies a custom retry strategy to use. | N/A |
retryIf(Closure $condition) |
Custom callback to determine if a retry should occur. | N/A |
retryUnless(Closure $condition) |
Custom callback to determine when a retry should NOT occur. | N/A |
withProgress(Closure $callback) |
Register callback for progress reporting during retries. | N/A |
withEventCallbacks(array $callbacks) |
Register callbacks for retry lifecycle events. | N/A |
withMetadata(array $metadata) |
Add custom data to the retry context. | N/A |
Understanding Delay Configuration
The delay between retry attempts is primarily controlled by the retry strategy used. The most important parameter across many strategies is baseDelay
, which serves as the foundation for delay calculations:
- What is
baseDelay
? A floating-point value (in seconds) that defines the starting point for calculating delays between retry attempts. - Default values: Default values for
baseDelay
(and other strategy options) are configured per strategy alias inconfig/retry.php
within thestrategies
array. For example, the defaultbaseDelay
forexponential-backoff
might be0.1
, while forfixed-delay
it might be1.0
. These defaults are used when a strategy is invoked via its alias (e.g., when set as thedefault
strategy in the config, or used as aninner_strategy
for wrappers like Circuit Breaker).
How baseDelay
is interpreted depends on the strategy:
- For
ExponentialBackoffStrategy
, it's the starting value that gets multiplied exponentially with each attempt (e.g., with multiplier 2.0: 0.1s, 0.2s, 0.4s, 0.8s...) - For
FixedDelayStrategy
, it's the consistent delay used between each retry (e.g., always waitsbaseDelay
seconds) - For
LinearBackoffStrategy
, it's the starting point before increments are added (e.g., with increment 0.5: 0.5s, 1.0s, 1.5s...) - For wrapper strategies (like
CircuitBreakerStrategy
,RateLimitStrategy
), thebaseDelay
concept usually applies to their inner strategy. The configuration for these wrappers often involves specifying the alias of the inner strategy (seeconfig/retry.php
examples).
You can configure baseDelay
(and other strategy options) in several ways, listed by precedence (later options override earlier ones):
- Globally per Strategy Alias: Define the default constructor parameters, including
baseDelay
, for each strategy alias inconfig/retry.php
under thestrategies
key (e.g.,config('retry.strategies.exponential-backoff.baseDelay')
). - When Instantiating a Strategy Directly: Pass
baseDelay
as a named argument to the strategy's constructor (e.g.,new ExponentialBackoffStrategy(baseDelay: 0.5)
). - In HTTP Client Macros: Use the
base_delay
option within the options array passed to macros likerobustRetry
orwithRetryStrategy
(e.g.,Http::robustRetry(3, null, ['base_delay' => 0.75])
). - In Pipeline Stages: Provide a custom
RetryStrategy
instance within a pipeline stage class, configured with its specificbaseDelay
in its constructor.
Example with different strategies (direct instantiation):
Most strategies in the library also support additional configuration parameters like maxDelay
, multiplier
, increment
, withJitter
, etc., that can be configured similarly (in config/retry.php
under the specific strategy alias or via constructor parameters) to further customize the delay behavior. Refer to the config/retry.php
file and the individual strategy classes for available options.
HTTP Client Integration
Laravel Retry extends Laravel's HTTP Client with custom macros like robustRetry
, withCircuitBreaker
, and withRateLimitHandling
. These macros are automatically registered when the package is installed, allowing you to use sophisticated retry patterns directly in your HTTP requests:
Note: The
max_attempts
parameter in HTTP client macros specifies the total number of attempts (initial + retries), whereasRetry::maxRetries()
specifies the number of additional retries after the first attempt.
Additional HTTP Client Macros
Laravel Retry provides other specialized macros for common patterns:
Circuit Breaker
Use the Circuit Breaker pattern to prevent overwhelming failing services:
Rate Limiting
Control the rate of requests to avoid overwhelming services:
These macros can be combined with other Laravel HTTP client features:
Pipeline Integration
For complex workflows where multiple steps need retry capabilities, use the RetryablePipeline
:
Each stage in the pipeline can have its own retry configuration by defining public properties:
retryCount
: Maximum number of retriesretryStrategy
: Custom retry strategy (configured with its own baseDelay)timeout
: Maximum time per attemptadditionalPatterns
: Additional exception patterns to retry onadditionalExceptions
: Additional exception types to retry on
Advanced Configuration
Retry Strategies
Laravel Retry comes with a comprehensive set of retry strategies to handle different scenarios. Each strategy implements the RetryStrategy
interface and can be used with the Retry
facade, HTTP client, or pipeline integration.
Strategy Overview
Strategy | Alias | Primary Use Case |
---|---|---|
ExponentialBackoffStrategy | exponential-backoff |
Handles general temporary failures by exponentially increasing the delay between retries. |
LinearBackoffStrategy | linear-backoff |
Provides a predictable retry delay that increases by a fixed amount with each attempt. |
FibonacciBackoffStrategy | fibonacci-backoff |
Offers a balanced retry delay growth based on the Fibonacci sequence, suitable for various scenarios. |
FixedDelayStrategy | fixed-delay |
Applies a consistent, fixed delay between every retry attempt, ideal for predictable recovery times. |
DecorrelatedJitterStrategy | decorrelated-jitter |
Prevents retry collisions ("thundering herd") in high-traffic scenarios using AWS-style decorrelated jitter. |
GuzzleResponseStrategy | guzzle-response |
Intelligently retries HTTP requests based on standard response headers like Retry-After or X-RateLimit-Reset . |
ResponseContentStrategy | response-content |
Triggers retries by inspecting response content (like JSON error codes or text patterns) even when the HTTP status is successful. |
CircuitBreakerStrategy | circuit-breaker |
Prevents overwhelming a failing service by temporarily halting requests after repeated failures (Circuit Breaker pattern). |
RateLimitStrategy | rate-limit |
Controls retry frequency to respect API rate limits or manage load on internal services using Laravel's Rate Limiter. |
TotalTimeoutStrategy | total-timeout |
Ensures the entire retry operation (including delays) completes within a specific total time limit. |
CustomOptionsStrategy | custom-options |
Allows customizing an existing strategy's behavior with specific options and callbacks for one-off adjustments without extending base classes. Perfect for specific, fine-grained control over retry logic using closures. |
CallbackRetryStrategy | callback-retry |
Enables completely custom retry logic by defining both the delay calculation and the retry decision logic via callbacks. |
Available strategies:
1. ExponentialBackoffStrategy (Alias: exponential-backoff
)
Increases delay exponentially with each attempt. Best for general-purpose retries and temporary networking or service issues where increasing wait times helps recovery.
2. LinearBackoffStrategy (Alias: linear-backoff
)
Increases delay by a fixed amount with each attempt. Useful when a more predictable increase in wait time is desired compared to exponential backoff.
3. FibonacciBackoffStrategy (Alias: fibonacci-backoff
)
Increases delay according to the Fibonacci sequence. Good balance between aggressive and conservative retries, growing slower than exponential but faster than linear initially.
4. FixedDelayStrategy (Alias: fixed-delay
)
Uses the same delay for every retry attempt. Ideal when the expected recovery time is consistent or when predictable delays are needed.
5. DecorrelatedJitterStrategy (Alias: decorrelated-jitter
)
Implements AWS-style jitter for better distribution of retries. Excellent for high-traffic scenarios to prevent the "thundering herd" problem where many clients retry simultaneously.
6. GuzzleResponseStrategy (Alias: guzzle-response
)
Intelligent HTTP retry strategy that respects response headers. Perfect for APIs that provide retry guidance through headers like Retry-After
or X-RateLimit-Reset
.
7. ResponseContentStrategy (Alias: response-content
)
Inspects response bodies for error conditions, even on successful status codes. Use for APIs that return success HTTP codes (200 OK) but signal errors via JSON response body.
8. CircuitBreakerStrategy (Alias: circuit-breaker
)
Implements the Circuit Breaker pattern to prevent overwhelming failing services. After a threshold of failures, it "opens" and temporarily stops attempts, allowing the service to recover.
9. RateLimitStrategy (Alias: rate-limit
)
Uses Laravel's Rate Limiter to control retry attempts. Ideal for respecting API rate limits or managing load on internal services.
10. TotalTimeoutStrategy (Alias: total-timeout
)
Enforces a maximum total duration for the entire retry operation. Use when an operation must complete within a strict time budget, regardless of individual attempt results.
11. CustomOptionsStrategy (Alias: custom-options
)
Allows customizing an existing strategy's behavior with specific options and callbacks for one-off adjustments without extending base classes. Perfect for specific, fine-grained control over retry logic using closures.
12. CallbackRetryStrategy (Alias: callback-retry
)
A fully callback-driven strategy where you define both the delay calculation and the retry decision logic via closures. Ideal for completely custom retry patterns without needing a base strategy.
Combining Strategies
Many strategies can be combined by wrapping one strategy inside another:
Note on Wrapper Strategies: When using wrapper strategies (like CircuitBreaker, RateLimit, TotalTimeout) via aliases or HTTP macros, configuration options like
baseDelay
typically apply to the default inner strategy. When instantiating wrappers directly as shown above, configure the inner strategy explicitly with its own parameters.
Exception Handling
Laravel Retry provides a sophisticated exception handling system that determines whether a failure should trigger a retry attempt. The system is extensible and configurable to match your specific needs.
Exception Handler Discovery
The package uses an automatic discovery mechanism to find and register exception handlers from:
- Built-in handlers in the package
- Custom handlers in your application's
app/Exceptions/Retry/Handlers
directory - Any additional directories specified in the
handler_paths
array inconfig/retry.php
Each handler determines which specific exceptions or error patterns should trigger a retry attempt.
Built-in Exception Handling
By default, the package includes handlers for common scenarios:
The built-in handlers cover common cases like:
- HTTP connection errors (timeouts, DNS failures, etc.)
- Rate limiting responses (429 Too Many Requests)
- Server errors (500, 502, 503, 504)
- Database deadlocks and lock timeouts
- Temporary network issues
Custom Retry Conditions
You can define custom conditions for retrying:
Example combining a custom handler with retryIf
:
Creating Custom Exception Handlers
For more reusable exception handling, implement the RetryableExceptionHandler
interface:
-
Create a new handler in
app/Exceptions/Retry/Handlers
: - The handler will be automatically discovered and registered by the
ExceptionHandlerManager
.
Adding Custom Handler Paths
If you want to organize your handlers in different locations, add the paths to the handler_paths
array in config/retry.php
:
Disabling Specific Handlers
You can temporarily disable specific handler types by extending the base handler and overriding the isApplicable()
method:
Events & Monitoring
Laravel Retry dispatches events at key points in the retry lifecycle, allowing you to monitor and respond to retry operations.
Available Events
Laravel Retry dispatches the following events:
RetryingOperationEvent
: Dispatched before each retry attempt, contains information about the current attempt, the delay before the retry, and the exception that caused the retry.OperationSucceededEvent
: Dispatched when the operation succeeds, contains the final result, the number of attempts, and performance metrics.OperationFailedEvent
: Dispatched when all retries are exhausted and the operation ultimately fails, contains the final exception, exception history, and performance metrics.
Each event includes a RetryContext
object containing detailed information about the retry operation.
The RetryContext Object
The RetryContext
object provides comprehensive information about the retry operation:
Property/Method | Description |
---|---|
getOperationId() |
Returns the unique identifier for this specific retry operation |
getMetadata() |
Returns any custom data added via withMetadata() |
getMetrics() |
Returns performance metrics including total_duration , average_duration , total_delay , min_duration , max_duration , and total_elapsed_time for monitoring and analytics. Details: |
total_duration
: Sum of execution time for each attempt (excluding delays)average_duration
: Average execution time per attempttotal_delay
: Sum of time spent waiting between retry attemptsmin_duration
/max_duration
: Minimum/Maximum execution time observed across attemptstotal_elapsed_time
: Total wall-clock time from start to completion | |getExceptionHistory()
| Returns an array of exceptions caught during previous attempts | |getMaxRetries()
| Returns the maximum number of retries configured for this operation | |getStartTime()
| Returns the timestamp when the retry operation started | |getTotalAttempts()
| Returns the total number of attempts made (including the initial attempt) | |getTotalDelay()
| Returns the total time spent waiting between retry attempts | |getSummary()
| Returns a summary array of retry operation statistics |
Practical Event Use Cases
Events provide powerful hooks into the retry lifecycle, enabling various monitoring and operational tasks:
- Logging: Track retry attempts, successes, and failures for audit trails and debugging
- Alerting: Send notifications to Slack or other platforms when operations consistently fail
- Metrics: Submit metrics to monitoring systems like Prometheus/Datadog to visualize retry patterns
- Resource Management: Release locks or clean up resources when operations complete
- Dynamic Configuration: Adjust retry parameters based on external conditions or previous attempt results
Using Events
Register listeners in your EventServiceProvider
:
Or use inline event callbacks:
Example Event Listener
Here's an example of a listener for the OperationFailedEvent
:
Troubleshooting
Here are solutions to common issues you might encounter:
Operation Isn't Retrying When Expected
- Check Exception Handlers: Ensure your exception type is covered by active handlers. Publish handlers with
php artisan vendor:publish --tag="retry-handlers"
to inspect built-in logic. - Check Conditions: If using
retryIf
orretryUnless
, verify your closure returns the correct boolean value. - Verify Max Retries: Ensure
maxRetries
in your config (or.maxRetries()
call) is greater than 0. - Check Base Delay: Verify the
baseDelay
configuration (inconfig/retry.php
or strategy constructors) if the delay between retries seems incorrect.
Custom Exception Handler Not Being Used
- Path: Make sure your handler is in
app/Exceptions/Retry/Handlers
or a path listed inconfig('retry.handler_paths')
. - Class Name: Ensure it ends with
Handler
(e.g.,CustomApiHandler.php
). - Interface: Verify it implements
GregPriday\LaravelRetry\Contracts\RetryableExceptionHandler
. - Applicability: Make sure
isApplicable()
returnstrue
when expected.
HTTP Client Macros Not Working
- Service Provider: Ensure
GregPriday\LaravelRetry\Http\HttpClientServiceProvider
is registered. - Parameters: Double-check the parameters. For example,
robustRetry
takesmaxAttempts
(including first attempt), while theRetry
facade usesmaxRetries
(additional attempts after the first).
Pipeline Stage Retries Using Incorrect Settings
- Property Names: Verify you're using the correct property names in your stage class:
retryCount
,retryStrategy
,timeout
, etc. - Initialization: Make sure custom strategies in stages are properly initialized in constructors.
Events Not Firing
- Config: Ensure
dispatch_events
is set totrue
inconfig/retry.php
. - Listeners: Verify your event listeners are correctly registered in your
EventServiceProvider
.
If problems persist, check your Laravel logs (storage/logs/laravel.log
) and consider enabling the withProgress()
callback for more verbose output during retries.
Contributing
We welcome contributions to Laravel Retry! Here's how you can help:
Reporting Issues
If you discover a bug or have a feature request:
- Search the GitHub issues to see if it has already been reported.
- If not, create a new issue with as much detail as possible.
Pull Requests
- Fork the repository
- Create a new branch for your feature or bug fix
- Write tests for your changes
- Ensure all tests pass by running
vendor/bin/phpunit
- Ensure code style compliance by running
vendor/bin/pint
- Submit a pull request with a clear description of your changes
Development Setup
Code Style
This package follows the Laravel coding style. We use Laravel Pint for code formatting:
License
Laravel Retry is open-sourced software licensed under the MIT license.