I just released Laravel Fuse on stage at Laracon India 2026. It's a package I built to solve a problem that's bitten me more than once: queue workers grinding to a halt when an external service goes down.
The Problem
Picture this scenario. It's 11 PM and Stripe is having issues. Your queue workers don't know that. They keep trying to charge customers, and each job waits 30 seconds for a timeout before failing. Then it retries. Waits again. Times out again.
If you have 10,000 payment jobs queued up and each one waits 30 seconds before timing out, you're looking at over 25 hours to clear the queue—even though every single request is going to fail.
Fuse fixes this by implementing the circuit breaker pattern. After a configurable number of failures, it stops making requests entirely. Jobs fail in milliseconds instead of waiting for timeouts, and they're automatically released back to the queue for later retry. When the service recovers, Fuse detects it and resumes normal operations.
How the Circuit Breaker Works
The circuit breaker has three states:
CLOSED is normal operation. All requests pass through to the external service. In the background, Fuse tracks success and failure rates using minute-based buckets that expire automatically.
OPEN is protection mode. Once the failure rate exceeds your configured threshold, the circuit trips. Jobs now fail immediately—no API call is made, no 30-second timeout. The job is released back to the queue with a delay. Your queue keeps moving.
HALF-OPEN is the recovery test. After a timeout period (configurable per service), Fuse allows one probe request through. If it succeeds, the circuit closes and normal operations resume. If it fails, the circuit reopens and waits again.
Installation
composer require harris21/laravel-fuse
Publish the configuration file:
php artisan vendor:publish --tag=fuse-config
Basic Usage
Add the middleware to any job that calls an external service:
use Harris21\Fuse\Middleware\CircuitBreakerMiddleware; class ChargeCustomer implements ShouldQueue{ public $tries = 0; public $maxExceptions = 3; public function middleware(): array { return [new CircuitBreakerMiddleware('stripe')]; } public function handle(): void { Stripe::charges()->create([ 'amount' => $this->amount, 'currency' => 'usd', 'customer' => $this->customerId, ]); }}
Setting $tries = 0 allows unlimited releases (since released jobs aren't "retries" in Laravel's sense), while $maxExceptions = 3 caps actual failures. The job itself doesn't need any changes—Fuse wraps around it.
Configuration
The published config file lets you set defaults and per-service overrides:
// config/fuse.php return [ 'enabled' => env('FUSE_ENABLED', true), 'default_threshold' => 50, 'default_timeout' => 60, 'default_min_requests' => 10, 'services' => [ 'stripe' => [ 'threshold' => 50, 'timeout' => 30, 'min_requests' => 5, ], 'mailgun' => [ 'threshold' => 60, 'timeout' => 120, 'min_requests' => 10, ], ],];
The threshold is a failure rate percentage. If 50% of requests fail within the tracking window, the circuit opens. The timeout is how many seconds to wait before testing recovery. And min_requests prevents the circuit from tripping on small sample sizes—you need at least this many requests before the failure rate is evaluated.
Intelligent Failure Classification
Not every error means a service is down. If Stripe returns a 429 because you're hitting rate limits, that's not an outage—the service is working fine, you're just sending too many requests. Same with authentication errors.
Fuse only counts failures that indicate actual service problems:
- 500, 502, 503 server errors count as failures
- Connection timeouts and refused connections count as failures
- 429 rate limits do not count
- 401 and 403 auth errors do not count
This prevents false positives. Your circuit won't trip just because someone deployed with an expired API key.
Peak Hours Support
During business hours, you might want to be more tolerant of failures to maximize successful transactions. Outside business hours, you might prefer earlier protection. Fuse supports this:
'stripe' => [ 'threshold' => 40, 'peak_hours_threshold' => 60, 'peak_hours_start' => 9, 'peak_hours_end' => 17,],
Between 9 AM and 5 PM, the circuit uses the 60% threshold. Outside those hours, it uses 40%. This lets you tune the tradeoff between protection and throughput based on when transactions matter most.
Events
Fuse dispatches Laravel events on every state transition:
use Harris21\Fuse\Events\CircuitBreakerOpened;use Harris21\Fuse\Events\CircuitBreakerHalfOpen;use Harris21\Fuse\Events\CircuitBreakerClosed;
You can listen to these for alerting:
class AlertOnCircuitOpen{ public function handle(CircuitBreakerOpened $event): void { Log::critical("Circuit opened for {$event->service}", [ 'failure_rate' => $event->failureRate, 'attempts' => $event->attempts, 'failures' => $event->failures, ]); // Send to Slack, page on-call, etc. }}
The CircuitBreakerOpened event includes the service name, current failure rate, total attempts, and failure count. This gives you what you need for debugging and alerting.
Direct Usage
You can also use the circuit breaker outside of queued jobs:
use Harris21\Fuse\CircuitBreaker; $breaker = new CircuitBreaker('stripe'); if (!$breaker->isOpen()) { try { $result = Stripe::charges()->create([...]); $breaker->recordSuccess(); return $result; } catch (Exception $e) { $breaker->recordFailure($e); throw $e; }} else { return $this->fallbackResponse();}
You can also check state and reset manually:
$breaker->isClosed();$breaker->isOpen();$breaker->isHalfOpen();$breaker->getStats();$breaker->reset();
Thundering Herd Prevention
When a circuit enters HALF-OPEN, you don't want 50 queue workers all sending probe requests simultaneously. Fuse uses Cache::lock() to ensure only one worker tests the service. The others continue failing fast until the probe completes.
Requirements
- PHP 8.3+
- Laravel 11+
- Any Laravel cache driver (Redis recommended for production)
The package has no external dependencies. It uses Laravel's native cache system for tracking and locks.
Links
- GitHub: harris21/laravel-fuse
The circuit breaker pattern comes from Michael Nygard's Release It! and was later popularized by Martin Fowler. Fuse brings it to Laravel with native integration and zero configuration for basic use cases.