Skip to content
This repository has been archived by the owner on Aug 17, 2022. It is now read-only.

Commit

Permalink
Merge pull request #6 from loveOSS/refactored-circuit-breaker
Browse files Browse the repository at this point in the history
[RFC] Refactored circuit breaker
  • Loading branch information
mickaelandrieu committed May 16, 2019
2 parents 360bc81 + 83ecc2f commit 26f9cba
Show file tree
Hide file tree
Showing 15 changed files with 327 additions and 230 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ script:
- composer cs-fix
- composer phpstan
- composer psalm
- ./vendor/bin/phpqa --report --ignoredDirs vendor
- composer phpqa

after_success:
- bash <(curl -s https://codecov.io/bash)
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
}
],
"require": {
"php": "^7.2"
"php": "^7.2",
"ext-json": "*"
},
"require-dev": {
"edgedesign/phpqa": "v1.21.1",
"friendsofphp/php-cs-fixer": "^2.12",
"guzzlehttp/guzzle": "^6.3",
"jakub-onderka/php-parallel-lint": "^1.0",
"mikey179/vfsStream": "^1.6",
"phpstan/phpstan": "^0.11.5",
"phpstan/phpstan-phpunit": "^0.11.0",
"phpunit/phpunit": "^8.0",
Expand Down
20 changes: 20 additions & 0 deletions src/Contracts/TransitionDispatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Resiliency\Contracts;

/**
* A Transition dispatcher is in charge of send information
* about the Circuit Breaker outside of the system.
*/
interface TransitionDispatcher
{
/**
* Dispatch a Circuit Breaker transition.
*
* @param CircuitBreaker $circuitBreaker the Circuit Breaker
* @param string $transition the Circuit Breaker transition name
* @param string $service the URI service called
* @param array $parameters the service parameters
*/
public function dispatch(CircuitBreaker $circuitBreaker, $transition, $service, array $parameters): void;
}
17 changes: 16 additions & 1 deletion src/Events/TransitionEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

namespace Resiliency\Events;

use Resiliency\Contracts\CircuitBreaker;
use Symfony\Component\EventDispatcher\Event;

class TransitionEvent extends Event
{
/**
* @var CircuitBreaker the Circuit Breaker
*/
private $circuitBreaker;

/**
* @var string the Transition name
*/
Expand All @@ -26,13 +32,22 @@ class TransitionEvent extends Event
* @param string $service the Service URI
* @param array $parameters the Service parameters
*/
public function __construct($eventName, $service, array $parameters)
public function __construct(CircuitBreaker $circuitBreaker, $eventName, $service, array $parameters)
{
$this->circuitBreaker = $circuitBreaker;
$this->eventName = $eventName;
$this->service = $service;
$this->parameters = $parameters;
}

/**
* @return CircuitBreaker the Circuit Breaker
*/
public function getCircuitBreaker(): CircuitBreaker
{
return $this->circuitBreaker;
}

/**
* @return string the Transition name
*/
Expand Down
107 changes: 95 additions & 12 deletions src/PartialCircuitBreaker.php → src/MainCircuitBreaker.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace Resiliency;

use Resiliency\Contracts\TransitionDispatcher;
use Resiliency\Transactions\SimpleTransaction;
use Resiliency\Exceptions\UnavailableService;
use Resiliency\Contracts\CircuitBreaker;
use Resiliency\Contracts\Transaction;
use Resiliency\Contracts\Storage;
Expand All @@ -11,43 +13,100 @@
use Resiliency\Contracts\Place;
use DateTime;

abstract class PartialCircuitBreaker implements CircuitBreaker
/**
* Main implementation of the Circuit Breaker.
*/
final class MainCircuitBreaker implements CircuitBreaker
{
public function __construct(
System $system,
Client $client,
Storage $storage
Storage $storage,
TransitionDispatcher $transitionDispatcher
) {
$this->currentPlace = $system->getInitialPlace();
$this->places = $system->getPlaces();
$this->client = $client;
$this->storage = $storage;
$this->transitionDispatcher = $transitionDispatcher;
}

/**
* @var Client the Client that consumes the service URI
*/
protected $client;
private $client;

/**
* @var Place the current Place of the Circuit Breaker
*/
protected $currentPlace;
private $currentPlace;

/**
* @var Place[] the Circuit Breaker places
*/
protected $places = [];
private $places;

/**
* @var Storage the Circuit Breaker storage
*/
protected $storage;
private $storage;

/**
* @var TransitionDispatcher the Circuit Breaker transition dispatcher
*/
private $transitionDispatcher;

/**
* {@inheritdoc}
*/
abstract public function call(string $service, callable $fallback, array $serviceParameters = []): string;
public function call(string $service, callable $fallback, array $serviceParameters = []): string
{
$transaction = $this->initTransaction($service, $serviceParameters);

if ($this->isOpened()) {
if (!$this->canAccessService($transaction)) {
return (string) $fallback();
}

$transaction = $this->moveStateTo(States::HALF_OPEN_STATE, $service);
$this->dispatch(
Transitions::CHECKING_AVAILABILITY_TRANSITION,
$service,
$serviceParameters
);
}

try {
$response = $this->request($service, $serviceParameters);
$this->moveStateTo(States::CLOSED_STATE, $service);
$this->dispatch(
Transitions::CLOSING_TRANSITION,
$service,
$serviceParameters
);

return $response;
} catch (UnavailableService $exception) {
$transaction->incrementFailures();
$this->storage->saveTransaction($service, $transaction);

if (!$this->isAllowedToRetry($transaction)) {
$this->moveStateTo(States::OPEN_STATE, $service);

$transition = Transitions::OPENING_TRANSITION;

if ($this->isHalfOpened()) {
$transition = Transitions::REOPENING_TRANSITION;
}

$this->dispatch($transition, $service, $serviceParameters);

return (string) $fallback();
}

return $this->call($service, $fallback, $serviceParameters);
}
}

/**
* {@inheritdoc}
Expand Down Expand Up @@ -87,7 +146,7 @@ public function isClosed(): bool
*
* @return Transaction
*/
protected function moveStateTo($state, $service): Transaction
private function moveStateTo($state, $service): Transaction
{
$this->currentPlace = $this->places[$state];
$transaction = SimpleTransaction::createFromPlace(
Expand All @@ -102,15 +161,18 @@ protected function moveStateTo($state, $service): Transaction

/**
* @param string $service the service URI
* @param array $serviceParameters the service UI parameters
*
* @return Transaction
*/
protected function initTransaction(string $service): Transaction
private function initTransaction(string $service, $serviceParameters): Transaction
{
if ($this->storage->hasTransaction($service)) {
$transaction = $this->storage->getTransaction($service);
$this->currentPlace = $this->places[$transaction->getState()];
} else {
$this->dispatch(Transitions::INITIATING_TRANSITION, $service, $serviceParameters);

$transaction = SimpleTransaction::createFromPlace(
$this->currentPlace,
$service
Expand All @@ -127,7 +189,7 @@ protected function initTransaction(string $service): Transaction
*
* @return bool
*/
protected function isAllowedToRetry(Transaction $transaction): bool
private function isAllowedToRetry(Transaction $transaction): bool
{
return $transaction->getFailures() < $this->currentPlace->getFailures();
}
Expand All @@ -137,7 +199,7 @@ protected function isAllowedToRetry(Transaction $transaction): bool
*
* @return bool
*/
protected function canAccessService(Transaction $transaction): bool
private function canAccessService(Transaction $transaction): bool
{
return $transaction->getThresholdDateTime() < new DateTime();
}
Expand All @@ -150,8 +212,10 @@ protected function canAccessService(Transaction $transaction): bool
*
* @return string
*/
protected function request(string $service, array $parameters = []): string
private function request(string $service, array $parameters = []): string
{
$this->dispatch(Transitions::TRIAL_TRANSITION, $service, $parameters);

return $this->client->request(
$service,
array_merge($parameters, [
Expand All @@ -160,4 +224,23 @@ protected function request(string $service, array $parameters = []): string
])
);
}

/**
* Helper to dispatch transition events.
*
* @param string $transition the transition name
* @param string $service the URI service called
* @param array $parameters the service parameters
*/
private function dispatch($transition, $service, array $parameters): void
{
$this->transitionDispatcher
->dispatch(
$this,
$transition,
$service,
$parameters
)
;
}
}
53 changes: 0 additions & 53 deletions src/SimpleCircuitBreaker.php

This file was deleted.

12 changes: 7 additions & 5 deletions src/SimpleCircuitBreakerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

namespace Resiliency;

use Resiliency\TransitionDispatchers\SimpleDispatcher;
use Resiliency\Contracts\CircuitBreaker;
use Resiliency\Contracts\Factory;
use Resiliency\Storages\SimpleArray;
use Resiliency\Clients\GuzzleClient;
use Resiliency\Systems\MainSystem;
use Resiliency\Storages\SimpleArray;
use Resiliency\Contracts\Factory;

/**
* Main implementation of Circuit Breaker Factory
* Used to create a SimpleCircuitBreaker instance.
* Used to create a basic CircuitBreaker instance.
*/
final class SimpleCircuitBreakerFactory implements Factory
{
Expand All @@ -24,10 +25,11 @@ public function create(array $settings): CircuitBreaker
$clientSettings = array_key_exists('client', $settings) ? (array) $settings['client'] : [];
$client = new GuzzleClient($clientSettings);

return new SimpleCircuitBreaker(
return new MainCircuitBreaker(
$mainSystem,
$client,
new SimpleArray()
new SimpleArray(),
new SimpleDispatcher('php://stdout')
);
}
}
Loading

0 comments on commit 26f9cba

Please sign in to comment.