Skip to content

Commit 457d602

Browse files
author
Gabriel Anhaia
committed
docs: overhaul README, add CHANGELOG; remove Travis; set composer version 2.0.0
1 parent b8938a0 commit 457d602

File tree

6 files changed

+99
-125
lines changed

6 files changed

+99
-125
lines changed

.idea/php-circuit-breaker.iml

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/php.xml

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.travis.yml

Lines changed: 0 additions & 22 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Changelog
2+
3+
## 2.0.0
4+
- Require PHP 8.1+
5+
- Replace `eloquent/enumeration` with native PHP enums
6+
- New `GabrielAnhaia\\PhpCircuitBreaker\\CircuitStateEnum`
7+
- `CircuitBreakerAdapter::getState()` returns `CircuitStateEnum`
8+
- Update tests for PHPUnit 10
9+
- Migrate CI to GitHub Actions
10+
- Redis operations use `setEx` for TTL handling
11+
- Documentation overhaul
12+
13+
## 1.x
14+
- PHP 7.4+/8.0+ support
15+
- `CircuitState` backed by `eloquent/enumeration`
16+
- Travis CI
17+
- Initial release and documentation

README.md

Lines changed: 77 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,112 @@
1-
[![Build Status](https://travis-ci.com/gabrielanhaia/php-circuit-breaker.svg?branch=master)](https://travis-ci.com/gabrielanhaia/php-circuit-breaker)
2-
![Code Coverage](https://img.shields.io/badge/coverage-100%25-green)
1+
![CI](https://github.com/gabrielanhaia/php-circuit-breaker/actions/workflows/ci.yml/badge.svg)
32
![Licence](https://img.shields.io/badge/licence-MIT-blue)
4-
![Package Stars](https://img.shields.io/badge/stars-%E2%98%85%E2%98%85%E2%98%85%E2%98%85%E2%98%85-yellow)
53

64
# PHP Circuit Breaker
75

8-
## Description
6+
Resiliency pattern for PHP microservices. This library implements a circuit breaker using Redis for state tracking. It protects your services from cascading failures by stopping calls to unhealthy dependencies and gradually allowing traffic as they recover.
97

10-
PHP Circuit Breaker was developed based on the book "Release It!: Design and Deploy Production-Ready Software (Pragmatic Programmers)", written by Michael T. Nygard.
11-
In this book, Michael popularized the Circuit Breaker.
8+
Learn more about circuit breakers: https://martinfowler.com/bliki/CircuitBreaker.html
129

13-
When we work with microservices, it is sometimes common to call these systems, and they are not available, which ends up causing problems in our application. To prevent any problem on our side, and guarantee that a service will not be called loads of times, we should use a Circuit Breaker.
10+
## Features
11+
- Simple API: `canPass`, `succeed`, `failed`
12+
- Redis-backed state with configurable windows/timeouts
13+
- Pluggable alert hook to notify when circuits open
14+
- PHP 8.1+ native enum for states in v2
1415

15-
You can find more information about Circuit Breakers [here](https://martinfowler.com/bliki/CircuitBreaker.html).
16+
## Versions
17+
- 2.x (current): PHP 8.1+, native enums, PHPUnit 10, GitHub Actions
18+
- 1.x (legacy): PHP 7.4+/8.0+, uses `eloquent/enumeration` and Travis CI
1619

17-
## Requirements
18-
19-
- PHP 8.1+ (library runtime also supports PHP 7.4, but development tools target PHP 8.1+)
20-
- Redis
21-
- Redis PHP extension enabled
22-
- Composer
23-
24-
___
25-
26-
## Installation
27-
28-
You can install **PHP Circuit Breaker** by composer running:
29-
```# composer require gabrielanhaia/php-circuit-breaker```
20+
See CHANGELOG for details and migration notes.
3021

22+
## Requirements
23+
- PHP 8.1+
24+
- Redis server
25+
- `ext-redis` PHP extension
3126

32-
___
33-
34-
## How do I use it?
35-
36-
I strongly recommend that you use a service container (dependency injection container) to deal with the objects and their dependencies, so you will be able to use it efficiently (It will not be necessary to create instances everywhere).
37-
38-
1. The first thing you can do is to define the *Settings*:
27+
## Install
28+
- v2 (recommended): `composer require gabrielanhaia/php-circuit-breaker:^2.0`
29+
- v1 (legacy): `composer require gabrielanhaia/php-circuit-breaker:^1.0`
3930

31+
## Quick Start
4032
```php
33+
use GabrielAnhaia\PhpCircuitBreaker\Adapter\Redis\RedisCircuitBreaker;
34+
use GabrielAnhaia\PhpCircuitBreaker\CircuitBreaker;
35+
4136
$settings = [
42-
'exceptions_on' => false, // Define if exceptions will be thrown when the circuit is open.
43-
'time_window' => 20, // Time window in which errors accumulate (Are being accounted for in total).
44-
'time_out_open' => 30, // Time window that the circuit will be opened (If opened).
45-
'time_out_half_open' => 20, // Time out that the circuit will be half-open.
46-
'total_failures' => 5 // Number of failures necessary to open the circuit.
37+
'exceptions_on' => false,
38+
'time_window' => 20,
39+
'time_out_open' => 30,
40+
'time_out_half_open' => 20,
41+
'total_failures' => 5,
4742
];
48-
```
49-
50-
*Note: It is not necessary to define these settings (they are the default values), they will be defined automatically.*
51-
52-
2. Instantiating a driver (Only Redis driver is available at the moment) and Redis client:
53-
54-
```php
55-
$redis = new \Redis;
56-
$redis->connect('localhost');
57-
$redisCircuitBreakerDriver = new GabrielAnhaia\PhpCircuitBreaker\Adapter\Redis\RedisCircuitBreaker($redis);
58-
59-
```
60-
61-
3. Instantiating the **PHP Circuit Breaker** class:
6243

63-
```php
64-
$circuitBreaker = new GabrielAnhaia\PhpCircuitBreaker\CircuitBreaker($redisCircuitBreakerDriver, $settings)
65-
```
66-
*Note: The second parameter is optional.*
44+
$redis = new \Redis();
45+
$redis->connect('127.0.0.1', 6379);
6746

68-
4. Validating if the circuit is open:
47+
$driver = new RedisCircuitBreaker($redis);
48+
$cb = new CircuitBreaker($driver, $settings);
6949

70-
```php
71-
if ($circuitBreaker->canPass($serviceName) !== true) {
50+
$service = 'PAYMENTS_API';
51+
if (!$cb->canPass($service)) {
52+
// Short-circuit
7253
return;
7354
}
74-
```
75-
76-
You can use the function **canPass** in any way you want. It will always return *true* when the Circuit is **CLOSED** or **HALF_OPEN**.
77-
After that, you should call your service, and depending on the response, you can call the following methods to update the circuit control variables.
7855

79-
If Success:
80-
```php
81-
$circuitBreaker->succeed($serviceName);
82-
```
83-
84-
If failure:
85-
```php
86-
$circuitBreaker->failed($serviceName);
56+
try {
57+
// Call dependency...
58+
$cb->succeed($service);
59+
} catch (\Throwable $e) {
60+
$cb->failed($service);
61+
}
8762
```
8863

89-
With these three simple methods, you can control the flow of your application in execution time.
90-
91-
### PHP 8.1+ Native Enum (Optional)
92-
93-
For projects on PHP 8.1 or newer, a native enum `GabrielAnhaia\PhpCircuitBreaker\CircuitStateEnum` is available. It mirrors the three states (`OPEN`, `CLOSED`, `HALF_OPEN`) and can be used in your own code to represent circuit state values. The existing `GabrielAnhaia\PhpCircuitBreaker\CircuitState` remains for backward compatibility and will be considered for replacement in a future major version.
94-
95-
96-
___
64+
## Configuration
65+
- `exceptions_on` (bool): throw when circuit is open (default: false)
66+
- `time_window` (int): seconds to track failures (default: 20)
67+
- `time_out_open` (int): seconds to keep circuit open (default: 30)
68+
- `time_out_half_open` (int): additional seconds before half-open closes (default: 20)
69+
- `total_failures` (int): failures within window to open (default: 5)
9770

98-
## Recap
71+
## Circuit State
72+
In v2+, states are represented with a native enum: `GabrielAnhaia\PhpCircuitBreaker\CircuitStateEnum` with cases `OPEN`, `CLOSED`, `HALF_OPEN`.
73+
You don’t normally need to consume this directly unless you’re writing custom adapters.
9974

100-
Let's say that you are using the following settings:
75+
## Alerts
76+
Implement `GabrielAnhaia\PhpCircuitBreaker\Contract\Alert` and pass it to `CircuitBreaker` to receive callbacks when a circuit opens.
10177

10278
```php
103-
$settings = [
104-
'exceptions_on' => false, // Define if exceptions will be thrown when the circuit is open.
105-
'time_window' => 20, // Time window in which errors accumulate (Are being accounted for in total).
106-
'time_out_open' => 30, // Time window that the circuit will be opened (If opened).
107-
'time_out_half_open' => 60, // Time out that the circuit will be half-open.
108-
'total_failures' => 5 // Number of failures necessary to open the circuit.
109-
];
79+
use GabrielAnhaia\PhpCircuitBreaker\Contract\Alert;
80+
81+
class LoggerAlert implements Alert {
82+
public function emmitOpenCircuit(string $serviceName)
83+
{
84+
error_log("Circuit opened: {$serviceName}");
85+
}
86+
}
11087
```
11188

112-
One of your services is a Payment Gateway, and you try to call it in an interval of each 2 seconds for some reason.
113-
The first time you call the Gateway, it responds with a 200 (HTTP status code), and after you call the method "succeed" with a service identifier (You can create one for each service).
89+
## Redis Keys
90+
Keys are namespaced per service:
91+
- `circuit_breaker:{SERVICE}:open`
92+
- `circuit_breaker:{SERVICE}:half_open`
93+
- `circuit_breaker:{SERVICE}:total_failures:*`
11494

115-
On the second, third, fourth, fifth, and sixth call, the Gateway is unavailable, so you call the method "failed" again.
95+
## Migration (1.x → 2.x)
96+
Breaking changes:
97+
- Replace `CircuitState::OPEN()` style calls with native enum values if referenced in your code, e.g. `CircuitStateEnum::OPEN`.
98+
- `CircuitBreakerAdapter::getState(string): CircuitStateEnum` now returns the native enum.
11699

117-
The total of failers was 5, now the next time you call the method "canPass" it will return "false" and the service will not be called again.
118-
At this moment the circuit is open, it will stay "OPEN" for 30 seconds (time_out_open), and then it will change the state to "HALF_OPEN" at this moment you can try to call the service again, and if it fails it will be "OPEN" for more 30 seconds.
100+
Otherwise, `CircuitBreaker` public API is unchanged.
119101

120-
What happens if the first four attempts fail and the fifth is succeeded?
121-
Then, the counter will be reset.
102+
## Development
103+
- Run tests: `vendor/bin/phpunit --configuration phpunit.xml`
104+
- GitHub Actions runs on PHP 8.1–8.4
122105

123-
What is the setting "time_window" for?
124-
Each failure is stored on Redis and has an expiration date.
125-
If the first failure happened exaclty at 12:00:10 and the "time_window" is 30 seconds, so, after 12:00:40 this failure will not be counted in the total of failures considered to open the circuit.
126-
In short, to open the circuit, you must have X (total_failures) in an interval of Y (time_window) seconds.
106+
## License
107+
MIT
127108

109+
---
128110

129-
___
111+
Created by: Gabriel Anhaia — https://www.linkedin.com/in/gabrielanhaia
130112

131-
Created by: **Gabriel Anhaia** - [https://www.linkedin.com/in/gabrielanhaia](https://www.linkedin.com/in/gabrielanhaia)

composer.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
2-
"name": "gabrielanhaia/php-circuit-breaker",
3-
"description": "Circuit breaker for microservices developed in PHP.",
4-
"type": "library",
2+
"name": "gabrielanhaia/php-circuit-breaker",
3+
"description": "Circuit breaker for microservices developed in PHP.",
4+
"type": "library",
5+
"version": "2.0.0",
56
"require": {
67
"php": "^8.1",
78
"ext-redis": "*"

0 commit comments

Comments
 (0)