PHPackages                             chiragagg5k/circuit-breaker - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. chiragagg5k/circuit-breaker

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

chiragagg5k/circuit-breaker
===========================

A PHP Circuit Breaker implementation to prevent cascading failures

v0.2.1(1mo ago)099MITPHPPHP ^8.2CI passing

Since Apr 20Pushed 1mo agoCompare

[ Source](https://github.com/ChiragAgg5k/circuit-breaker)[ Packagist](https://packagist.org/packages/chiragagg5k/circuit-breaker)[ Docs](https://github.com/ChiragAgg5k/circuit-breaker)[ RSS](/packages/chiragagg5k-circuit-breaker/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (2)DependenciesVersions (4)Used By (0)

Circuit Breaker
===============

[](#circuit-breaker)

A lightweight PHP Circuit Breaker implementation to prevent cascading failures in distributed systems.

Installation
------------

[](#installation)

```
composer require chiragagg5k/circuit-breaker
```

What is a Circuit Breaker?
--------------------------

[](#what-is-a-circuit-breaker)

A Circuit Breaker is a design pattern used to detect failures and prevent an application from constantly trying to execute an operation that's likely to fail. This helps prevent cascading failures in distributed systems.

Features
--------

[](#features)

- ✅ Proper state management (CLOSED, OPEN, HALF\_OPEN)
- ✅ Half-open state with gradual recovery
- ✅ Configurable failure and success thresholds
- ✅ Automatic state transitions
- ✅ State inspection methods
- ✅ Simple and lightweight implementation
- ✅ Optional Redis and Swoole Table cache adapters for shared state
- ✅ Native telemetry metrics through optional `utopia-php/telemetry` adapters
- ✅ PSR-4 autoloading compatible
- ✅ PHP 8.2+ support with enums

Usage
-----

[](#usage)

### Basic Example

[](#basic-example)

```
use ChiragAgg5k\CircuitBreaker;

$breaker = new CircuitBreaker(
    threshold: 3,         // Open circuit after 3 failures
    timeout: 30,          // Try half-open after 30 seconds
    successThreshold: 2   // Require 2 successes to close circuit
);

$result = $breaker->call(
    open: fn() => "Service unavailable - circuit is open",
    close: fn() => makeExternalApiCall(),
    halfOpen: fn() => makeExternalApiCall() // Optional: called during recovery testing
);
```

### Using All Three States

[](#using-all-three-states)

```
use ChiragAgg5k\CircuitBreaker;

$breaker = new CircuitBreaker(threshold: 3, timeout: 30, successThreshold: 2);

$result = $breaker->call(
    open: function() {
        // Circuit is OPEN - service is down
        logger()->warning('Circuit breaker is OPEN - using fallback');
        return getCachedData() ?? ['error' => 'Service unavailable'];
    },
    close: function() {
        // Circuit is CLOSED - normal operation
        return apiClient()->fetchData();
    },
    halfOpen: function() {
        // Circuit is HALF_OPEN - testing recovery
        logger()->info('Circuit breaker testing recovery...');
        return apiClient()->fetchData(['timeout' => 5]); // Use shorter timeout
    }
);
```

### Real-world Example

[](#real-world-example)

```
use ChiragAgg5k\CircuitBreaker;

$breaker = new CircuitBreaker(threshold: 5, timeout: 60, successThreshold: 2);

$data = $breaker->call(
    open: function() {
        // Fallback when circuit is open
        return cache()->get('user_data') ?? ['error' => 'Service temporarily unavailable'];
    },
    close: function() {
        // Primary operation
        $response = Http::get('https://api.example.com/users');

        if (!$response->successful()) {
            throw new \Exception('API request failed');
        }

        return $response->json();
    }
);
```

### Shared Cache State

[](#shared-cache-state)

By default, each `CircuitBreaker` instance keeps state in memory. To share circuit state between PHP workers, pass a cache adapter and a stable `cacheKey`.

#### Redis

[](#redis)

```
use ChiragAgg5k\CircuitBreaker\Adapter\Redis as RedisAdapter;
use ChiragAgg5k\CircuitBreaker;

$redis = new \Redis();
$redis->connect('127.0.0.1');

$breaker = new CircuitBreaker(
    threshold: 5,
    timeout: 60,
    successThreshold: 2,
    cache: new RedisAdapter($redis),
    cacheKey: 'users-api'
);
```

#### Swoole Table

[](#swoole-table)

Use the Swoole adapter when workers need to share state through Swoole shared memory.

```
use ChiragAgg5k\CircuitBreaker\Adapter\SwooleTable;
use ChiragAgg5k\CircuitBreaker;

$table = SwooleTable::createTable(size: 1024);
$cache = new SwooleTable($table);

$breaker = new CircuitBreaker(
    threshold: 5,
    timeout: 60,
    successThreshold: 2,
    cache: $cache,
    cacheKey: 'users-api'
);
```

### Telemetry

[](#telemetry)

Telemetry is opt-in. The `telemetry` constructor argument defaults to `null`, which emits no metrics and does not require `utopia-php/telemetry` at runtime. Install `utopia-php/telemetry` and pass any adapter to emit counters and gauges for calls, fallbacks, callback failures, transitions, state, failure counts, success counts, active calls, and transition/probe events.

```
composer require utopia-php/telemetry
```

```
use ChiragAgg5k\CircuitBreaker;
use Utopia\Telemetry\Adapter\OpenTelemetry;

$telemetry = new OpenTelemetry(
    'http://otel-collector:4318/v1/metrics',
    'backend',
    'orders',
    gethostname() ?: 'local'
);

$breaker = new CircuitBreaker(
    threshold: 5,
    timeout: 60,
    successThreshold: 2,
    cacheKey: 'orders-api',
    telemetry: $telemetry,
    metricPrefix: 'backend'
);

$result = $breaker->call(
    open: fn () => ['fallback' => true],
    close: fn () => $client->request('/orders')
);

$telemetry->collect();
```

By default, metrics are emitted as `breaker.*`. Pass `metricPrefix` to namespace those metric names for a host application; for example `metricPrefix: 'backend'` emits `backend.breaker.calls`.

You can also attach or replace the adapter after construction:

```
$breaker = new CircuitBreaker(metricPrefix: 'backend');
$breaker->setTelemetry($telemetry);
```

How it Works
------------

[](#how-it-works)

The circuit breaker operates in three states:

1. **CLOSED State** (Normal Operation)

    - Requests pass through to the protected service
    - Failures are counted
    - When failures &gt;= threshold, transitions to OPEN
2. **OPEN State** (Blocking Requests)

    - All requests are immediately rejected (calls `open` callback)
    - After timeout period, transitions to HALF\_OPEN
    - Prevents overwhelming a failing service
3. **HALF\_OPEN State** (Testing Recovery)

    - Allows requests through to test if service recovered
    - Calls `halfOpen` callback if provided, otherwise uses `close` callback
    - Counts successful requests
    - If successes &gt;= successThreshold: transitions to CLOSED
    - If any failure occurs: immediately transitions back to OPEN

This gradual recovery mechanism prevents overwhelming a service that's just starting to recover. The optional `halfOpen` callback lets you apply different behavior during testing (e.g., shorter timeouts, reduced payload, logging).

Configuration
-------------

[](#configuration)

### Constructor Parameters

[](#constructor-parameters)

- `threshold` (int, default: 3): Number of failures before opening the circuit
- `timeout` (int, default: 30): Seconds to wait before transitioning to half-open state
- `successThreshold` (int, default: 2): Number of consecutive successes required to close the circuit from half-open state
- `cache` (`?ChiragAgg5k\CircuitBreaker\Adapter`, default: `null`): Optional shared cache adapter
- `cacheKey` (string, default: `default`): Cache namespace for one circuit's state
- `telemetry` (`?Utopia\Telemetry\Adapter`, default: `null`): Optional telemetry adapter
- `metricPrefix` (string, default: `''`): Optional prefix for telemetry metric names, such as `edge`

### Call Method Parameters

[](#call-method-parameters)

```
$breaker->call(
    open: callable,      // Required: Called when circuit is OPEN
    close: callable,     // Required: Called when circuit is CLOSED (or HALF_OPEN if no halfOpen callback)
    halfOpen: ?callable  // Optional: Called when circuit is HALF_OPEN for recovery testing
);
```

### State Inspection Methods

[](#state-inspection-methods)

```
// Check current state
$state = $breaker->getState();  // Returns CircuitState enum

// Boolean checks
$breaker->isOpen();      // true if circuit is open
$breaker->isClosed();    // true if circuit is closed
$breaker->isHalfOpen();  // true if circuit is half-open

// Get metrics
$breaker->getFailureCount();  // Current failure count
$breaker->getSuccessCount();  // Current success count (in half-open state)
```

Requirements
------------

[](#requirements)

- PHP 8.2 or higher
- Optional: `utopia-php/telemetry`, `ext-opentelemetry`, and `ext-protobuf` for OpenTelemetry metrics and the local telemetry demo
- Optional: `ext-redis` for `ChiragAgg5k\CircuitBreaker\Adapter\Redis`
- Optional: `ext-swoole` for `ChiragAgg5k\CircuitBreaker\Adapter\SwooleTable`

Testing
-------

[](#testing)

Unit tests avoid Redis and Swoole runtime dependencies:

```
composer test
```

E2E tests run Redis and a PHP runtime with Redis/Swoole extensions through Docker:

```
composer test:e2e:docker
```

Local Telemetry Demo
--------------------

[](#local-telemetry-demo)

Run the local demo stack to start Redis, an instrumented PHP demo server, OpenTelemetry Collector, Prometheus, and Grafana:

```
composer telemetry:up
```

- Demo UI:
- Grafana:
- Prometheus:

Preview from a five-minute `checkout-api` scenario:

[![Circuit breaker telemetry dashboard](docs/images/telemetry-dashboard.png)](docs/images/telemetry-dashboard.png)

Populate the dashboard with the same scenario:

```
composer telemetry:scenario
```

Stop the stack and remove local volumes:

```
composer telemetry:down
```

License
-------

[](#license)

MIT License

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance90

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community3

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

49d ago

PHP version history (2 changes)v0.1.0PHP ^8.1

v0.2.0PHP ^8.2

### Community

---

Top Contributors

[![ChiragAgg5k](https://avatars.githubusercontent.com/u/110609663?v=4)](https://github.com/ChiragAgg5k "ChiragAgg5k (12 commits)")

### Embed Badge

![Health badge](/badges/chiragagg5k-circuit-breaker/health.svg)

```
[![Health](https://phpackages.com/badges/chiragagg5k-circuit-breaker/health.svg)](https://phpackages.com/packages/chiragagg5k-circuit-breaker)
```

###  Alternatives

[gabrieliuga/laravel-nova-sidebar-links

Allow definition of custom links on Laravel Nova sidebar.

12198.0k](/packages/gabrieliuga-laravel-nova-sidebar-links)[blacksmoke26/yii2cdn

A Yii Framework 2 component for using assets in different environments (Local/CDNs)

1412.3k](/packages/blacksmoke26-yii2cdn)[workbunny/webman-ip-attribution

Webman plugin workbunny/webman-ip-attribution.

282.5k](/packages/workbunny-webman-ip-attribution)[rami/seo-bundle

All in One Symfony Seo Bundle

341.5k](/packages/rami-seo-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
