Weave 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

Circuit Breaker Laravel Package

ksaveras/circuit-breaker

A circuit breaker for PHP/Laravel that protects your app from repeated failures when calling external services. It tracks failures, opens the circuit to fail fast, and automatically recovers after a cooldown, helping prevent cascading outages and improve resilience.

View on GitHub
Deep Wiki
Context7

Getting Started

This package (ksaveras/circuit-breaker) is a framework-agnostic PHP circuit breaker — it does not ship a Laravel service provider, so your first task is wiring it up yourself.

composer require ksaveras/circuit-breaker

Minimum requirements after v2.1.2: PHP 8.2+ (the release bumps Symfony components to 7.2.0), plus either the redis extension / predis or an in-memory store for local work.

Where to look first:

  • README.md for the builder API (CircuitBreaker::builder()->...->build()).
  • src/Storage/ for the storage adapters (Redis, In-Memory, and any PSR-backed adapters).
  • src/Policy/ for retry policies (ConstantRetryPolicy, ExponentialBackoffRetryPolicy).
  • src/Event/ for the lifecycle events (CircuitBreakerClosed, CircuitBreakerOpen, CircuitBreakerHalfOpen, Failed, Succeeded).

First use case — protect a single flaky outbound dependency (a payment gateway, a partner API) and fail fast instead of queueing up timeouts:

$breaker = CircuitBreaker::builder()
    ->storage(app(InMemory::class))   // swap for Redis in production
    ->failureThreshold(5)
    ->timeWindow(60)
    ->build('payments-api');

$result = $breaker->call(fn () => Http::get('https://payments.example.com/ping')->json());

Implementation Patterns

One breaker per downstream dependency. Create a named breaker for each external service, not one global breaker. The name is the key in storage; colliding names merge failure counts across unrelated services.

Bind breakers as singletons in a service provider.

// AppServiceProvider::register()
$this->app->singleton('breaker.payments', fn ($app) => CircuitBreaker::builder()
    ->storage($app->make(RedisStorage::class))
    ->failureThreshold(10)
    ->timeWindow(30)
    ->build('payments'));

Then inject by name, or wrap the breaker behind a typed gateway class (PaymentsClient) so application code never touches the builder API.

Wrap the call, not the retry. The breaker should wrap the unit of work (one HTTP call, one job dispatch), while backoff/retry lives in a retry policy on the breaker or in Laravel's Http::retry(). Don't double up.

Use lifecycle events for observability. Dispatch the package events into Laravel's event system (bridge the Symfony dispatcher in your service provider), then listen for CircuitBreakerOpen to fire a Slack alert or log an incident. This is the cheapest way to get visibility into trips.

Queue jobs as a natural fit. In a job, call the breaker around the remote work; if the breaker is open, release the job with a backoff instead of letting it fail and burn retry attempts.

Testing. Use InMemory storage in tests and assert on the breaker behavior directly — trip it with N failing calls, assert the next call short-circuits without hitting the dependency. Never point tests at the Redis-backed storage.

Gotchas and Tips

No Laravel integration — you own the lifecycle. There is no facade, no auto-registered config, and no config file. Everything (storage driver, thresholds, wiring) is manual. Budget an hour for a clean gateway wrapper rather than sprinkling builder calls in controllers.

v2.1.2 Symfony 7.2 bump can cause composer conflicts. Laravel 10 apps pin Symfony components to 6.x; if the package's transitive symfony/* requirement tightens to ^7.2, composer may refuse to install alongside Laravel 10. Test composer update on a branch before bumping prod; Laravel 11+ (Symfony 7) is clean. If you're stuck on Laravel 10, pin the previous package version and note the constraint.

Storage choice matters more than thresholds. In-memory storage is per-process — it resets on every request and every queue worker, so it's not a circuit breaker in production (each worker has its own failing counter). Use Redis-backed storage for shared state, and ensure the Redis client is a singleton, not resolved per call.

Tune timeWindow against your actual latency profile. A window shorter than your dependency's p99 timeout means you never accumulate enough failures to trip; too long and you stay open after recovery. Start with failureThreshold(5) and timeWindow(60) and revisit after observing real traffic.

Open breakers throw. Your $breaker->call() will throw once the circuit is open (a CircuitBreakerException/service-unavailable exception). Handle it explicitly — return a degraded response, enqueue for later, or surface a 503 — rather than letting it bubble into a generic 500.

Register listeners on the real event names. If you bridge the Symfony event dispatcher, verify the exact Ksaveras\CircuitBreaker\Event\* class names are registered in EventServiceProvider; a typo silently means no alerts fire.

Add a manual reset path. Give ops an artisan command or admin endpoint to reset a breaker's state (delete its storage key) so you don't wait out the window after resolving an incident.

Events are extension points. If you need per-attempt logging, metrics, or audit trails, listen to CircuitBreakerFailed/CircuitBreakerSucceeded rather than wrapping the callable.

Don't use it as a retry library. The retry policy is a convenience; the breaker's job is fail-fast. Keep them conceptually separate in your mental model and your code.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource