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

Delay Exponential Backoff Bundle Laravel Package

avtonom/delay-exponential-backoff-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require avtonom/exponential-backoff-bundle
    

    Add to config/bundles.php (Symfony 4+):

    return [
        // ...
        Avtonom\ExponentialBackoffBundle\AvtonomExponentialBackoffBundle::class => ['all' => true],
    ];
    
  2. Configure (optional) in config/packages/avtonom_exponential_backoff.yaml:

    avtonom_exponential_backoff:
        cap: 1000000000  # 1 second in microseconds
        max_attempts: 5
    
  3. First Use Case: Inject the service in a controller/service and use it for retries:

    use Avtonom\ExponentialBackoffBundle\Service\ExponentialBackoffService;
    
    class MyService {
        public function __construct(private ExponentialBackoffService $backoff) {}
    
        public function retryOperation() {
            $attempt = 1;
            while (true) {
                try {
                    $this->callExternalService();
                    break;
                } catch (Exception $e) {
                    if ($attempt >= $this->backoff->getMaxAttempts()) {
                        throw $e;
                    }
                    $this->backoff->delay($attempt);
                    $attempt++;
                }
            }
        }
    }
    

Implementation Patterns

Common Workflows

  1. Retry Logic in Services:

    public function fetchDataWithRetry() {
        $attempt = 1;
        while (true) {
            try {
                return $this->fetchData();
            } catch (RateLimitException $e) {
                if ($attempt >= $this->backoff->getMaxAttempts()) {
                    throw new RetryFailedException('Max retries exceeded', 0, $e);
                }
                $this->backoff->equalJitter($attempt); // Add jitter for randomness
                $attempt++;
            }
        }
    }
    
  2. Command Bus Integration: Use with Symfony Messenger or similar for async retries:

    $message = new MyMessage();
    $this->bus->dispatch($message);
    
    // In handler:
    public function __invoke(MyMessage $message) {
        $attempt = 1;
        while (true) {
            try {
                $this->process($message);
                break;
            } catch (Exception $e) {
                if ($attempt >= $this->backoff->getMaxAttempts()) {
                    throw $e;
                }
                $this->backoff->fullJitter($attempt);
                $attempt++;
                $this->bus->dispatch($message); // Requeue
            }
        }
    }
    
  3. Middleware for HTTP Clients: Wrap Guzzle/HTTP client calls:

    $client = new Client([
        'handler' => HandlerStack::create([
            new RetryMiddleware($this->backoff),
            GuzzleHttp\HandlerStack::create(),
        ]),
    ]);
    

Integration Tips

  • Dependency Injection: Prefer constructor injection for ExponentialBackoffService.
  • Configuration: Override defaults via config/packages/ for environment-specific tuning.
  • Testing: Mock the service to simulate delays:
    $this->backoff->shouldReceive('equalJitter')->with(1)->andReturn(100000); // 0.1s
    

Gotchas and Tips

Pitfalls

  1. Microseconds vs Seconds:

    • All methods return microseconds. Convert to seconds for sleep():
      sleep($this->backoff->exponential($attempt) / 1_000_000);
      
    • Or use usleep() directly:
      usleep($this->backoff->halfDelay($attempt));
      
  2. Max Attempts = 0:

    • Default max_attempts: 0 means no limit. Set explicitly (e.g., max_attempts: 3) to avoid infinite loops.
  3. Cap Overrides:

    • If cap is too low, delays may truncate unexpectedly. Test edge cases:
      $this->backoff->exponential(10); // May return capped value if cap=1000000
      
  4. Thread Safety:

    • Not thread-safe by design. Use per-request instances or synchronize access in multi-threaded environments.

Debugging

  • Log Delays:
    $delay = $this->backoff->fullJitter($attempt);
    $this->logger->debug('Retry delay', ['attempt' => $attempt, 'microseconds' => $delay]);
    
  • Console Command: Use php bin/console exponential-backoff to verify calculations before integrating.

Extension Points

  1. Custom Strategies: Extend the service or create decorators:

    class CustomBackoffService extends ExponentialBackoffService {
        public function customDelay($attempt) {
            return min(parent::exponential($attempt) * 1.5, $this->getCap());
        }
    }
    
  2. Event-Based Retries: Dispatch events on retry attempts for observability:

    $this->eventDispatcher->dispatch(
        new RetryAttemptEvent($attempt, $delay),
        RetryAttemptEvent::NAME
    );
    
  3. Dynamic Configuration: Override getDefaultOptions() in a custom service to fetch values from a database or API.

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
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
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