Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Backoff Laravel Package

eventsauce/backoff

Small PHP library with a BackOffStrategy interface and ready-made retry delays (exponential, Fibonacci, linear). Configure initial delay, max tries, max delay, and growth base. Call backOff($tries, $throwable) inside retry loops to pause between attempts.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture Fit

  • Pros:

    • Decoupled Design: The package enforces a clean separation between retry logic and business logic, aligning with Laravel’s dependency injection (DI) and service container patterns. The BackOffStrategy interface allows for strategy injection, making it easy to swap implementations (e.g., exponential, Fibonacci) without modifying core logic.
    • Flexibility: Supports custom retry conditions (e.g., exception types, return values) and integrates seamlessly with Laravel’s exception handling (e.g., App\Exceptions\Handler). This fits well with Laravel’s event-driven and middleware-heavy architecture.
    • Jitter Support: Mitigates thundering herd problems in distributed systems (e.g., microservices, queues), which is critical for Laravel applications interacting with external APIs or databases.
    • BackOffRunner: Provides a high-level abstraction for lazy developers (or teams), reducing boilerplate for common retry patterns (e.g., run(function () { ... })).
  • Cons:

    • Goto Statements: The manual retry loop uses goto, which is unconventional in PHP/Laravel and may violate team coding standards. The BackOffRunner mitigates this but isn’t always applicable (e.g., async jobs, middleware).
    • No Built-in Circuit Breaker: While backoff is useful, a circuit breaker (e.g., stopping retries after repeated failures) would complement this package. Laravel’s Illuminate\Bus\PendingDispatchException or third-party packages (e.g., spatie/circuit-breaker) could fill this gap.
    • PHP 8.1+ Requirement: May exclude legacy Laravel projects (pre-8.1) without polyfills.

Integration Feasibility

  • Laravel-Specific Opportunities:
    • Queue Jobs: Ideal for retrying failed Laravel queue jobs (e.g., Illuminate\Queue\ShouldQueue). The BackOffRunner can wrap job logic in handle() methods.
    • HTTP Clients: Integrates with Laravel’s HTTP client (Illuminate\Support\Facades\Http) for retrying API calls with jittered delays.
    • Database Operations: Useful for retrying database transactions or Eloquent operations (e.g., DB::transaction()).
    • Event Listeners: Retry failed event publishing or listening (e.g., dispatchSync()).
    • Middleware: Custom middleware to retry failed requests (e.g., API rate limits).
  • Example Use Cases:
    • External API Calls: Retry failed requests to third-party services with exponential backoff + jitter.
    • Payment Processing: Retry failed Stripe/PayPal transactions with Fibonacci backoff.
    • Webhooks: Retry failed webhook deliveries (e.g., Slack, GitHub).

Technical Risk

  • Low Risk:
    • Mature Design: The package is battle-tested (used by EventSaucePHP, a DDD/CQRS framework) and follows SOLID principles.
    • Minimal Overhead: Lightweight (~10KB) with no external dependencies.
    • Laravel Synergy: Works well with Laravel’s DI container, queues, and HTTP client.
  • Moderate Risk:
    • Performance Impact: Exponential backoff can delay critical paths (e.g., user-facing requests). Mitigate by:
      • Using BackOffRunner for non-critical operations (e.g., background jobs).
      • Configuring short maxTries and initialDelay for HTTP requests.
    • Debugging Complexity: Retries with jitter may obscure root causes of failures. Log retry attempts (e.g., using Laravel’s Log facade) and track metrics (e.g., max_retries_reached events).
  • High Risk:
    • Infinite Retries: Setting maxTries: -1 risks unbounded delays. Avoid in production; use maxTries conservatively (e.g., 3–5).
    • Stateful Retries: If retries involve external state (e.g., database locks), deadlocks or race conditions may occur. Use transactions or optimistic locking.

Key Questions

  1. Where to Apply Retries?
    • Prioritize external dependencies (APIs, databases) over internal logic.
    • Avoid retries for idempotent operations (e.g., GET requests) unless necessary.
  2. Strategy Selection:
    • Exponential: Default for transient failures (e.g., network timeouts).
    • Fibonacci: Better for bursty failures (e.g., rate-limited APIs).
    • Linear: Predictable delays (e.g., polling).
  3. Jitter Configuration:
    • Use FullJitter for high-contention scenarios (e.g., shared resources).
    • Use HalfJitter or ScatteredJitter for finer control.
  4. Monitoring:
    • How will you track retry metrics (e.g., success/failure rates, max delays)?
    • Will you integrate with Laravel’s Sentry or Laravel Debugbar?
  5. Fallbacks:
    • What happens after maxTries is exhausted? (e.g., log, notify, fail silently)
    • Should retries trigger alerts (e.g., Slack, PagerDuty)?
  6. Testing:
    • How will you test retry logic? (e.g., mock external dependencies, verify backoff delays)
    • Will you use Laravel’s HttpTests or QueueTests for integration tests?

Integration Approach

Stack Fit

  • Core Laravel Components:
    • Service Container: Inject BackOffStrategy into controllers, services, or jobs.
    • Queues: Wrap job logic in BackOffRunner for async retries.
    • HTTP Client: Use with Http::retry() or custom middleware.
    • Events: Retry failed event dispatching (e.g., Bus::dispatch()).
    • Artisan Commands: Retry CLI operations (e.g., schedule:run).
  • Third-Party Packages:
    • Laravel Horizon: Retry failed queue jobs with custom backoff.
    • Spatie Laravel Activitylog: Retry failed log entries.
    • Laravel Telescope: Monitor retry attempts and delays.
  • Example Integration:
    // app/Providers/AppServiceProvider.php
    public function register()
    {
        $this->app->bind(BackOffStrategy::class, function () {
            return new ExponentialBackOffStrategy(
                initialDelayMs: 100,
                maxTries: 3,
                jitter: new FullJitter()
            );
        });
    }
    

Migration Path

  1. Phase 1: Pilot Integration
    • Start with a single high-impact use case (e.g., payment processing).
    • Use BackOffRunner to minimize code changes.
    • Example:
      // app/Jobs/ProcessPayment.php
      public function handle()
      {
          $runner = new BackOffRunner(
              new ExponentialBackOffStrategy(100, 3),
              PaymentFailedException::class
          );
          $runner->run(fn() => $this->chargeCustomer());
      }
      
  2. Phase 2: Broaden Scope
    • Replace custom retry logic in services/controllers with BackOffStrategy.
    • Example:
      // app/Services/PaymentService.php
      public function __construct(
          private PaymentGateway $gateway,
          private BackOffStrategy $backOff
      ) {}
      
      public function charge(float $amount): void
      {
          $tries = 0;
          start:
          try {
              $this->gateway->charge($amount);
          } catch (PaymentFailedException $e) {
              $this->backOff->backOff(++$tries, $e);
              if ($tries < 3) goto start;
              throw $e;
          }
      }
      
  3. Phase 3: Standardize
    • Create a base trait/class for retryable operations (e.g., RetryableService).
    • Document retry strategies per service (e.g., config/backoff.php).
    • Example config:
      'strategies' => [
          'payments' => [
              'strategy' => ExponentialBackOffStrategy::class,
              'initial_delay' => 100, // ms
              'max_tries' => 3,
              'jitter' => FullJitter::class,
          ],
          'api_calls' => [
              'strategy' => FibonacciBackOffStrategy::class,
              'initial_delay' => 500,
              'max_tries' => 5,
              'jitter' => HalfJitter::class,
          ],
      ],
      

Compatibility

  • Laravel Versions: Compatible with Laravel 9+ (PHP 8.1+). For older versions, consider:
    • Polyfills for PHP 8.0 features (e.g., named arguments).
    • Forking the package or using a compatibility layer.
  • Package Conflicts: None identified. The package is self-contained.
  • Async Considerations:
    • Queues: Works seamlessly with Laravel queues (e.g., failed_jobs table).
    • Async Workers:
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
besmartand-pro/php-quality-config
sentix/ai-chatbot
codifyo/ts-generator-bundle
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor