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
strategiessection 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 thedefaultor as an inner strategy).- Dedicated sections for
circuit_breakerandresponse_contentprovide 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 customRetryableExceptionHandlerclasses 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.phpwithin thestrategiesarray. For example, the defaultbaseDelayforexponential-backoffmight be0.1, while forfixed-delayit might be1.0. These defaults are used when a strategy is invoked via its alias (e.g., when set as thedefaultstrategy in the config, or used as aninner_strategyfor 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 waitsbaseDelayseconds) - 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), thebaseDelayconcept usually applies to their inner strategy. The configuration for these wrappers often involves specifying the alias of the inner strategy (seeconfig/retry.phpexamples).
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.phpunder thestrategieskey (e.g.,config('retry.strategies.exponential-backoff.baseDelay')). - When Instantiating a Strategy Directly: Pass
baseDelayas a named argument to the strategy's constructor (e.g.,new ExponentialBackoffStrategy(baseDelay: 0.5)). - In HTTP Client Macros: Use the
base_delayoption within the options array passed to macros likerobustRetryorwithRetryStrategy(e.g.,Http::robustRetry(3, null, ['base_delay' => 0.75])). - In Pipeline Stages: Provide a custom
RetryStrategyinstance within a pipeline stage class, configured with its specificbaseDelayin 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_attemptsparameter 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
baseDelaytypically 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/Handlersdirectory - Any additional directories specified in the
handler_pathsarray 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
retryIforretryUnless, verify your closure returns the correct boolean value. - Verify Max Retries: Ensure
maxRetriesin your config (or.maxRetries()call) is greater than 0. - Check Base Delay: Verify the
baseDelayconfiguration (inconfig/retry.phpor 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/Handlersor 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()returnstruewhen expected.
HTTP Client Macros Not Working
- Service Provider: Ensure
GregPriday\LaravelRetry\Http\HttpClientServiceProvideris registered. - Parameters: Double-check the parameters. For example,
robustRetrytakesmaxAttempts(including first attempt), while theRetryfacade 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_eventsis set totrueinconfig/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.