-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Ricardo Fiorani
committed
Jul 18, 2019
0 parents
commit 9c18782
Showing
20 changed files
with
856 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
composer.lock | ||
vendor | ||
.idea | ||
.phpunit.result.cache | ||
coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# pirate-circuit-breaker | ||
|
||
[![Latest Version on Packagist][ico-version]][link-packagist] | ||
[![Software License][ico-license]](LICENSE.md) | ||
[![Build Status][ico-travis]][link-travis] | ||
[![Coverage Status][ico-scrutinizer]][link-scrutinizer] | ||
[![Quality Score][ico-code-quality]][link-code-quality] | ||
[![Total Downloads][ico-downloads]][link-downloads] | ||
|
||
This is an Implementation of the 2-state (Open and Closed) CircuitBreaker pattern that we use at HolidayPirates. | ||
Unlike the [3-state CircuitBreaker proposed by Fowler](https://martinfowler.com/bliki/CircuitBreaker.html), this implementation has only two states, "Open" and "Closed". | ||
|
||
## Install | ||
|
||
Via Composer | ||
|
||
```bash | ||
$ composer require holidaypirates/pirate-circuit-breaker | ||
``` | ||
|
||
## Requirements | ||
- PHP 7.3 | ||
- An implementation of the `\Psr\SimpleCache\CacheInterface` to store the services failures and circuit state OR your own storage implementation of `\HolidayPirates\CircuitBreaker\Storage\StorageInterface` | ||
- For development only : Docker and Docker-Compose | ||
## Usage | ||
|
||
```php | ||
<?php declare(strict_types=1); | ||
|
||
use HolidayPirates\CircuitBreaker\CircuitBreaker; | ||
use HolidayPirates\CircuitBreaker\Service\DummyService; | ||
use HolidayPirates\CircuitBreaker\Storage\Adapter\SimpleCacheAdapter; | ||
|
||
// Setup: | ||
$pool = new YourCachePool(); // Any implementation of \Psr\SimpleCache\CacheInterface | ||
$storageAdapter = new SimpleCacheAdapter($pool); | ||
$circuitBreaker = new CircuitBreaker($storageAdapter); | ||
|
||
$service = new DummyService(5, 60); //After 5 failed attempts it will wait 60 seconds before allowing more requests. | ||
|
||
$circuitBreaker->registerService($service); | ||
|
||
// Usage: | ||
$dummyApiClient = new DummyApiClient(); // This will be any service you want to protect with the CB | ||
|
||
if (false == $circuitBreaker->isServiceAvailable(DummyService::class)) { | ||
throw new \Exception('Service unavailable'); | ||
} | ||
|
||
try { | ||
$response = $dummyApiClient->sendRequest(); | ||
$circuitBreaker->reportSuccess(DummyService::class); | ||
} catch (Exception $exception) { | ||
$circuitBreaker->reportFailure(DummyService::class); | ||
|
||
throw new \Exception('Service unavailable',0, $exception); | ||
} | ||
|
||
``` | ||
> Please note that `HolidayPirates\CircuitBreaker\Service\DummyService` is just an implementation of `\HolidayPirates\CircuitBreaker\Service\ServiceInterface`. | ||
> You must create your own implementations of `\HolidayPirates\CircuitBreaker\Service\ServiceInterface` for each service that you want the CircuitBreaker to operate in. | ||
For more examples of usage please see `\HolidayPirates\Tests\Integration\CircuitBreaker\CircuitBreakerTest` | ||
## Testing | ||
|
||
```bash | ||
$ docker-compose run php vendor/bin/phpunit | ||
``` | ||
|
||
## Credits | ||
|
||
- [Ricardo Fiorani][link-author] | ||
- [All Contributors][link-contributors] | ||
|
||
## License | ||
|
||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information. | ||
|
||
[ico-version]: https://img.shields.io/packagist/v/holidaypirates/pirate-circuit-breaker.svg?style=flat-square | ||
[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square | ||
[ico-travis]: https://img.shields.io/travis/holidaypirates/pirate-circuit-breaker/master.svg?style=flat-square | ||
[ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/holidaypirates/pirate-circuit-breaker.svg?style=flat-square | ||
[ico-code-quality]: https://img.shields.io/scrutinizer/g/holidaypirates/pirate-circuit-breaker.svg?style=flat-square | ||
[ico-downloads]: https://img.shields.io/packagist/dt/holidaypirates/pirate-circuit-breaker.svg?style=flat-square | ||
|
||
[link-packagist]: https://packagist.org/packages/holidaypirates/pirate-circuit-breaker | ||
[link-travis]: https://travis-ci.org/holidaypirates/pirate-circuit-breaker | ||
[link-scrutinizer]: https://scrutinizer-ci.com/g/holidaypirates/pirate-circuit-breaker/code-structure | ||
[link-code-quality]: https://scrutinizer-ci.com/g/holidaypirates/pirate-circuit-breaker | ||
[link-downloads]: https://packagist.org/packages/holidaypirates/pirate-circuit-breaker | ||
[link-author]: https://github.com/ricardofiorani | ||
[link-contributors]: ../../contributors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
{ | ||
"name": "holidaypirates/pirate-circuit-breaker", | ||
"description": "Implementation of the 2-state CircuitBreaker pattern that we use at HolidayPirates", | ||
"homepage": "https://www.holidaypirates.group", | ||
"type": "library", | ||
"keywords": [ | ||
"circuit-breaker", | ||
"service", | ||
"protection" | ||
], | ||
"license": "MIT", | ||
"prefer-stable": true, | ||
"authors": [ | ||
{ | ||
"name": "Ricardo Fiorani", | ||
"email": "[email protected]" | ||
} | ||
], | ||
"autoload": { | ||
"psr-4": { | ||
"HolidayPirates\\CircuitBreaker\\": "src/" | ||
} | ||
}, | ||
"autoload-dev": { | ||
"psr-4": { | ||
"HolidayPirates\\Tests\\Integration\\CircuitBreaker\\": "tests/Integration", | ||
"HolidayPirates\\Tests\\Unit\\CircuitBreaker\\": "tests/Unit" | ||
} | ||
}, | ||
"require": { | ||
"php": ">=7.3", | ||
"psr/simple-cache": "^1.0" | ||
}, | ||
"require-dev": { | ||
"roave/security-advisories": "dev-master", | ||
"phpunit/phpunit": "^8.2", | ||
"cache/array-adapter": "^1.0", | ||
"cache/redis-adapter": "^1.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
version: '3' | ||
services: | ||
php: | ||
build: docker/php | ||
volumes: | ||
- .:/code | ||
working_dir: "/code" | ||
links: | ||
- redis | ||
|
||
redis: | ||
image: redis:5.0.4-alpine |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FROM php:7.3-fpm | ||
RUN pecl install xdebug-2.7.1 && pecl install redis-4.0.1 | ||
RUN docker-php-ext-enable xdebug redis | ||
|
||
WORKDIR "/code" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" | ||
bootstrap="vendor/autoload.php" | ||
colors="true"> | ||
<testsuites> | ||
<testsuite name="Integration"> | ||
<directory>./tests/Integration</directory> | ||
</testsuite> | ||
<testsuite name="Unit"> | ||
<directory>./tests/Unit</directory> | ||
</testsuite> | ||
</testsuites> | ||
<filter> | ||
<whitelist processUncoveredFilesFromWhitelist="true"> | ||
<directory suffix=".php">./src</directory> | ||
</whitelist> | ||
</filter> | ||
</phpunit> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace HolidayPirates\CircuitBreaker\Awareness; | ||
|
||
use HolidayPirates\CircuitBreaker\CircuitBreakerInterface; | ||
|
||
interface CircuitBreakerAwareInterface | ||
{ | ||
public function setCircuitBreaker(CircuitBreakerInterface $circuitBreaker): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace HolidayPirates\CircuitBreaker\Awareness; | ||
|
||
use HolidayPirates\CircuitBreaker\CircuitBreakerInterface; | ||
use HolidayPirates\CircuitBreaker\Exception\UnavailableServiceException; | ||
|
||
trait CircuitBreakerAwareTrait | ||
{ | ||
private $circuitBreaker; | ||
|
||
public function setCircuitBreaker(CircuitBreakerInterface $circuitBreaker): void | ||
{ | ||
$this->circuitBreaker = $circuitBreaker; | ||
} | ||
|
||
public function getCircuitBreaker(): CircuitBreakerInterface | ||
{ | ||
return $this->circuitBreaker; | ||
} | ||
|
||
/** | ||
* @throws UnavailableServiceException | ||
*/ | ||
public function throwExceptionIfServiceUnavailable(string $serviceName): void | ||
{ | ||
if (false === $this->getCircuitBreaker()->isServiceAvailable($serviceName)) { | ||
throw new UnavailableServiceException("Service {$serviceName} is not available right now."); | ||
} | ||
} | ||
|
||
public function reportServiceSuccess(string $serviceName): void | ||
{ | ||
$this->getCircuitBreaker()->reportSuccess($serviceName); | ||
} | ||
|
||
public function reportServiceFailure(string $serviceName): void | ||
{ | ||
$this->getCircuitBreaker()->reportFailure($serviceName); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace HolidayPirates\CircuitBreaker; | ||
|
||
use HolidayPirates\CircuitBreaker\Service\ServiceInterface; | ||
use HolidayPirates\CircuitBreaker\Storage\StorageInterface; | ||
|
||
class CircuitBreaker implements CircuitBreakerInterface | ||
{ | ||
private $storage; | ||
private $services = []; | ||
|
||
public function __construct(StorageInterface $storage) | ||
{ | ||
$this->storage = $storage; | ||
} | ||
|
||
public function registerService(ServiceInterface $service): void | ||
{ | ||
$this->services[$service->getIdentifier()] = $service; | ||
} | ||
|
||
public function isServiceAvailable(string $serviceName): bool | ||
{ | ||
$service = $this->getService($serviceName); | ||
|
||
if ($this->isCircuitOpen($service)) { | ||
return false; | ||
} | ||
|
||
/** | ||
* If we wanted a full 3-state CircuitBreaker implementation as described by Fowler, it is in here | ||
* that we can add the logic for the "Half-open circuit", which would allow a smaller number of requests | ||
* to go through before we really close the circuit. | ||
*/ | ||
|
||
return true; | ||
} | ||
|
||
public function reportFailure(string $serviceName): void | ||
{ | ||
$service = $this->getService($serviceName); | ||
$amountOfFailures = $this->storage->getAmountOfFailures($service); | ||
|
||
if ($amountOfFailures >= $service->getMaxFailures()) { | ||
$this->setOpenCircuit($service); | ||
|
||
return; | ||
} | ||
|
||
$this->storage->incrementAmountOfFailures($service); | ||
} | ||
|
||
public function reportSuccess(string $serviceName): void | ||
{ | ||
$service = $this->getService($serviceName); | ||
|
||
$this->storage->incrementAmountOfSuccess($service); | ||
} | ||
|
||
public function areAllServicesAvailable(): bool | ||
{ | ||
foreach ($this->getRegisteredServiceNames() as $serviceName) { | ||
if (!$this->isServiceAvailable($serviceName)) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
public function getRegisteredServiceNames(): array | ||
{ | ||
return array_keys($this->services); | ||
} | ||
|
||
private function getService(string $serviceName): ServiceInterface | ||
{ | ||
if (false === isset($this->services[$serviceName])) { | ||
throw new \LogicException( | ||
sprintf( | ||
'Service not found. Did you forgot to call registerService(%s) ?', | ||
$serviceName | ||
) | ||
); | ||
} | ||
|
||
return $this->services[$serviceName]; | ||
} | ||
|
||
private function setOpenCircuit(ServiceInterface $service): void | ||
{ | ||
$this->storage->setOpenCircuit($service); | ||
} | ||
|
||
private function isCircuitOpen(ServiceInterface $service): bool | ||
{ | ||
return $this->storage->isCircuitOpen($service); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace HolidayPirates\CircuitBreaker; | ||
|
||
use HolidayPirates\CircuitBreaker\Service\ServiceInterface; | ||
use HolidayPirates\CircuitBreaker\Storage\Adapter\Exception\StorageAdapterException; | ||
|
||
interface CircuitBreakerInterface | ||
{ | ||
public function registerService(ServiceInterface $service): void; | ||
|
||
/** | ||
* @throws StorageAdapterException | ||
*/ | ||
public function isServiceAvailable(string $serviceName): bool; | ||
|
||
/** | ||
* @throws StorageAdapterException | ||
*/ | ||
public function areAllServicesAvailable(): bool; | ||
|
||
/** | ||
* @throws StorageAdapterException | ||
*/ | ||
public function getRegisteredServiceNames(): array; | ||
|
||
/** | ||
* @throws StorageAdapterException | ||
*/ | ||
|
||
public function reportFailure(string $serviceName): void; | ||
|
||
/** | ||
* @throws StorageAdapterException | ||
*/ | ||
public function reportSuccess(string $serviceName): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace HolidayPirates\CircuitBreaker\Exception; | ||
|
||
use Exception; | ||
|
||
class UnavailableServiceException extends Exception | ||
{ | ||
|
||
} |
Oops, something went wrong.