ejsmont-artur/php-circuit-breaker
Laravel-friendly PHP circuit breaker implementation to add resiliency to external service calls. Supports configurable failure thresholds, timeouts and recovery, helping prevent cascading failures when APIs or dependencies are down or slow.
HttpClient facade). Can be wrapped around repositories, services, or controllers without invasive changes.handle() method.PaymentService, ThirdPartyApiClient).OrderShipped event calling a shipping API).| Risk Area | Assessment | Mitigation Strategy |
|---|---|---|
| State Management | The package relies on in-memory state (open/half-open/closed) by default. In distributed environments (e.g., Laravel Forge + multiple servers), this could lead to inconsistent states. | Use Redis or database-backed state storage (e.g., via php-circuit-breaker’s StateStorageInterface). Laravel’s cache() facade can also be adapted for persistence. |
| Configuration Complexity | Overly granular configurations (e.g., per-endpoint timeouts) may require boilerplate setup. | Leverage Laravel config files (config/circuit-breakers.php) to centralize settings. Use dependency injection to pass configurations dynamically. |
| Observability Gaps | Default implementation lacks metrics (e.g., Prometheus) or logging hooks. | Integrate with Laravel’s logging (Log::channel()) or monitoring tools (e.g., Datadog, New Relic) via custom state listeners. |
| Testing Overhead | Testing circuit breaker states (e.g., "half-open") requires mocking time and simulating failures, which can be cumbersome. | Use Laravel’s testing helpers (e.g., travel() for time manipulation) and Pest/PHPUnit to assert state transitions. |
| Performance Overhead | Circuit breaker checks add minimal latency (~1–5ms per call), but high-throughput systems may need optimization. | Benchmark in staging. For ultra-low-latency paths, consider bypassing the breaker for internal-only calls. |
stripe-api, payment-service) or global (e.g., all external calls)?laravel-retryable) or queue drivers?| Laravel Component | Integration Strategy | Example Implementation |
|---|---|---|
| HTTP Client | Wrap HttpClient calls (e.g., Http::post()) with the circuit breaker. |
```php |
| use EjsmontArtur\CircuitBreaker\CircuitBreaker; | ||
| use Illuminate\Support\Facades\Http; |
$breaker = new CircuitBreaker( fn() => Http::post('https://api.example.com/pay'), config('circuit-breakers.payment_timeout') ); $response = $breaker->execute();
| **Middleware** | Create a **Laravel middleware** to protect API routes calling external services. | ```php
public function handle(Request $request, Closure $next) {
$breaker = new CircuitBreaker(
fn() => $next($request),
config('circuit-breakers.api_timeout')
);
return $breaker->execute();
}
``` |
| **Queue Jobs** | Wrap job `handle()` methods or use **job middleware**. | ```php
// In job middleware
public function handle($job, Closure $next) {
$breaker = new CircuitBreaker(
fn() => $next($job),
config('circuit-breakers.job_timeout')
);
return $breaker->execute();
}
``` |
| **Service Layer** | Inject the circuit breaker into **services** (e.g., `PaymentService`). | ```php
class PaymentService {
public function __construct(
private CircuitBreaker $stripeBreaker
) {}
public function charge(ChargeRequest $request) {
return $this->stripeBreaker->execute(
fn() => Stripe::charges()->create($request->toArray())
);
}
}
``` |
| **Repositories** | Protect **database-heavy operations** (e.g., bulk inserts) that may fail under load. | ```php
class UserRepository {
public function __construct(
private CircuitBreaker $dbBreaker
) {}
public function bulkCreate(array $users) {
return $this->dbBreaker->execute(
fn() => DB::table('users')->insert($users)
);
}
}
``` |
| **Event Listeners** | Safeguard **event handlers** that trigger external actions. | ```php
public function handle(OrderShipped $event) {
$breaker = new CircuitBreaker(
fn() => $this->shippingApi->notify($event->order),
config('circuit-breakers.shipping_timeout')
);
$breaker->execute();
}
``` |
### **Migration Path**
1. **Phase 1: Pilot Integration**
- Start with **non-critical external APIs** (e.g., analytics, logging).
- Implement **middleware-based protection** for API routes.
- Validate state persistence (Redis/DB) in staging.
2. **Phase 2: Service Layer Adoption**
- Refactor **services** (e.g., `PaymentService`, `ThirdPartyApi`) to use circuit breakers.
- Add **fallback logic** (e.g., cached responses, admin alerts).
3. **Phase 3: Queue & Job Protection**
- Apply to **high-failure-rate jobs** (e.g., webhook retries, email sends).
- Monitor **queue backlogs** to ensure breakers aren’t masking deeper issues.
4. **Phase 4: Observability & Optimization**
- Integrate with **Laravel monitoring** (e.g., Horizon, Sentry).
- Tune **timeout thresholds** based on real-world latency data.
### **Compatibility**
| **Compatibility Factor** | **Assessment** | **Workaround** |
|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Laravel Version** | Works with **Laravel 8+** (PHP 7.4+). | For
How can I help you explore Laravel packages today?