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 #2 from loveOSS/improved-mainsystem
Browse files Browse the repository at this point in the history
Improved main system
  • Loading branch information
mickaelandrieu authored May 11, 2019
2 parents c3a2d7e + 2932c07 commit e5c315f
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 89 deletions.
15 changes: 3 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,12 @@ composer test
## Code quality

```
composer cs-fix && composer phpstan
composer cs-fix && composer phpstan && composer psalm
```

We also use [PHPQA](https://github.com/EdgedesignCZ/phpqa#phpqa) to check the Code quality
during the CI management of the contributions.

If you want to use it (using Docker):

```
docker run --rm -u $UID -v $(pwd):/app eko3alpha/docker-phpqa --report --ignoredDirs vendor
```

If you want to use it (using Composer):
during the CI management of the contributions:

```
composer global require edgedesign/phpqa=v1.20.0 --update-no-dev
phpqa --report --ignoredDirs vendor
composer phpqa
```
13 changes: 3 additions & 10 deletions src/SimpleCircuitBreaker.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

namespace Resiliency;

use Resiliency\Contracts\Place;
use Resiliency\Contracts\System;
use Resiliency\Contracts\Client;
use Resiliency\Systems\MainSystem;
use Resiliency\Storages\SimpleArray;
use Resiliency\Exceptions\UnavailableService;

Expand All @@ -13,14 +12,8 @@
*/
final class SimpleCircuitBreaker extends PartialCircuitBreaker
{
public function __construct(
Place $openPlace,
Place $halfOpenPlace,
Place $closedPlace,
Client $client
) {
$system = new MainSystem($closedPlace, $halfOpenPlace, $openPlace);

public function __construct(System $system, Client $client)
{
parent::__construct($system, $client, new SimpleArray());
}

Expand Down
12 changes: 3 additions & 9 deletions src/SimpleCircuitBreakerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@

use Resiliency\Contracts\CircuitBreaker;
use Resiliency\Contracts\Factory;
use Resiliency\Places\ClosedPlace;
use Resiliency\Places\HalfOpenPlace;
use Resiliency\Places\OpenPlace;
use Resiliency\Clients\GuzzleClient;
use Resiliency\Systems\MainSystem;

/**
* Main implementation of Circuit Breaker Factory
Expand All @@ -20,17 +18,13 @@ final class SimpleCircuitBreakerFactory implements Factory
*/
public function create(array $settings): CircuitBreaker
{
$openPlace = OpenPlace::fromArray((array) $settings['open']);
$halfOpenPlace = HalfOpenPlace::fromArray((array) $settings['half_open']);
$closedPlace = ClosedPlace::fromArray((array) $settings['closed']);
$mainSystem = MainSystem::createFromArray($settings);

$clientSettings = array_key_exists('client', $settings) ? (array) $settings['client'] : [];
$client = new GuzzleClient($clientSettings);

return new SimpleCircuitBreaker(
$openPlace,
$halfOpenPlace,
$closedPlace,
$mainSystem,
$client
);
}
Expand Down
17 changes: 17 additions & 0 deletions src/Systems/MainSystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use Resiliency\Contracts\Place;
use Resiliency\Contracts\System;
use Resiliency\Places\ClosedPlace;
use Resiliency\Places\HalfOpenPlace;
use Resiliency\Places\OpenPlace;
use Resiliency\States;

/**
Expand Down Expand Up @@ -47,4 +50,18 @@ public function getPlaces(): array
{
return $this->places;
}

/**
* @param array $settings the settings for the Places
*
* @return self
*/
public static function createFromArray(array $settings): self
{
$openPlace = OpenPlace::fromArray((array) $settings['open']);
$halfOpenPlace = HalfOpenPlace::fromArray((array) $settings['half_open']);
$closedPlace = ClosedPlace::fromArray((array) $settings['closed']);

return new self($closedPlace, $halfOpenPlace, $openPlace);
}
}
29 changes: 28 additions & 1 deletion tests/CircuitBreakerTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Tests\Resiliency;

use Resiliency\Clients\GuzzleClient;
use Resiliency\Systems\MainSystem;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\MockHandler;
use PHPUnit\Framework\TestCase;
Expand All @@ -21,7 +22,7 @@ abstract class CircuitBreakerTestCase extends TestCase
*
* @return GuzzleClient
*/
protected function getTestClient()
protected function getTestClient() : GuzzleClient
{
$mock = new MockHandler([
new RequestException('Service unavailable', new Request('GET', 'test')),
Expand All @@ -33,4 +34,30 @@ protected function getTestClient()

return new GuzzleClient(['handler' => $handler]);
}

/**
* Returns an instance of Main system shared by all the circuit breakers.
*
* @return MainSystem
*/
protected function getSystem(): MainSystem
{
return MainSystem::createFromArray(
[
'open' => [0, 0.0, 1.0], // threshold 1.0s
'half_open' => [0, 0.2, 0.0], // timeout 0.2s to test the service
'closed' => [2, 0.2, 0.0], // 2 failures allowed, 0.2s timeout
]
);
}

/**
* Will wait for X seconds, functional wrapper for sleep function.
*
* @param int $seconds The number of seconds
*/
protected function waitFor(int $seconds): void
{
sleep($seconds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,24 @@

namespace Tests\Resiliency;

use Symfony\Component\EventDispatcher\EventDispatcher;
use Resiliency\Contracts\CircuitBreaker;
use Resiliency\Storages\SymfonyCache;
use Resiliency\SymfonyCircuitBreaker;
use Resiliency\SimpleCircuitBreaker;
use Resiliency\Places\HalfOpenPlace;
use Resiliency\Places\ClosedPlace;
use Resiliency\Systems\MainSystem;
use Resiliency\Places\OpenPlace;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Cache\Simple\ArrayCache;

class CircuitBreakerTests extends CircuitBreakerTestCase
class CircuitBreakerWorkflowTest extends CircuitBreakerTestCase
{
/**
* When we use the circuit breaker on unreachable service
* the fallback response is used.
*
* @param CircuitBreaker $circuitBreaker
* @dataProvider getCircuitBreakers
*/
public function testCircuitBreakerIsInClosedStateAtStart($circuitBreaker)
public function testCircuitBreakerIsInClosedStateAtStart(CircuitBreaker $circuitBreaker): void
{
$this->assertSame('CLOSED', $circuitBreaker->getState());

Expand All @@ -37,15 +36,16 @@ public function testCircuitBreakerIsInClosedStateAtStart($circuitBreaker)
* Once the number of failures is reached, the circuit breaker
* is opened. This time no calls to the services are done.
*
* @param CircuitBreaker $circuitBreaker
* @depends testCircuitBreakerIsInClosedStateAtStart
* @dataProvider getCircuitBreakers
*/
public function testCircuitBreakerWillBeOpenedInCaseOfFailures($circuitBreaker)
public function testCircuitBreakerWillBeOpenedInCaseOfFailures(CircuitBreaker $circuitBreaker): void
{
// CLOSED
$circuitBreaker->call('https://httpbin.org/get/foo', $this->createFallbackResponse());

$this->assertSame('OPENED', $circuitBreaker->getState());
$this->assertSame('OPEN', $circuitBreaker->getState());
$this->assertSame(
'{}',
$circuitBreaker->call(
Expand All @@ -60,18 +60,19 @@ public function testCircuitBreakerWillBeOpenedInCaseOfFailures($circuitBreaker)
* try again to reach the service. This time, the service
* is not reachable.
*
* @param CircuitBreaker $circuitBreaker
* @depends testCircuitBreakerIsInClosedStateAtStart
* @depends testCircuitBreakerWillBeOpenedInCaseOfFailures
* @dataProvider getCircuitBreakers
*/
public function testAfterTheThresholdTheCircuitBreakerMovesInHalfOpenState($circuitBreaker)
public function testAfterTheThresholdTheCircuitBreakerMovesInHalfOpenState(CircuitBreaker $circuitBreaker): void
{
// CLOSED
$circuitBreaker->call('https://httpbin.org/get/foo', $this->createFallbackResponse());
// OPEN
$circuitBreaker->call('https://httpbin.org/get/foo', $this->createFallbackResponse());

sleep(2);
$this->waitFor(1);
// NOW HALF OPEN
$this->assertSame(
'{}',
Expand All @@ -80,27 +81,28 @@ public function testAfterTheThresholdTheCircuitBreakerMovesInHalfOpenState($circ
$this->createFallbackResponse()
)
);
$this->assertSame('HALF_OPENED', $circuitBreaker->getState());
$this->assertSame('HALF OPEN', $circuitBreaker->getState());
$this->assertTrue($circuitBreaker->isHalfOpened());
}

/**
* In HalfOpen state, if the service is back we can
* close the CircuitBreaker.
*
* @param CircuitBreaker $circuitBreaker
* @depends testCircuitBreakerIsInClosedStateAtStart
* @depends testCircuitBreakerWillBeOpenedInCaseOfFailures
* @depends testAfterTheThresholdTheCircuitBreakerMovesInHalfOpenState
* @dataProvider getCircuitBreakers
*/
public function testOnceInHalfOpenModeServiceIsFinallyReachable($circuitBreaker)
public function testOnceInHalfOpenModeServiceIsFinallyReachable(CircuitBreaker $circuitBreaker): void
{
// CLOSED
$circuitBreaker->call('https://httpbin.org/get/foo', $this->createFallbackResponse());
// OPEN
$circuitBreaker->call('https://httpbin.org/get/foo', $this->createFallbackResponse());

sleep(2);
$this->waitFor(1);
// NOW HALF OPEN
$this->assertSame(
'{}',
Expand All @@ -109,10 +111,11 @@ public function testOnceInHalfOpenModeServiceIsFinallyReachable($circuitBreaker)
$this->createFallbackResponse()
)
);
$this->assertSame('HALF_OPENED', $circuitBreaker->getState());
$this->assertSame('HALF OPEN', $circuitBreaker->getState());
$this->assertTrue($circuitBreaker->isHalfOpened());

sleep(2);
$this->waitFor(1);

// CLOSED
$this->assertSame(
'{"hello": "world"}',
Expand All @@ -131,43 +134,36 @@ public function testOnceInHalfOpenModeServiceIsFinallyReachable($circuitBreaker)
*
* @return array
*/
public function getCircuitBreakers()
public function getCircuitBreakers(): array
{
return [
'simple' => $this->createSimpleCircuitBreaker(),
'symfony' => $this->createSymfonyCircuitBreaker(),
'simple' => [$this->createSimpleCircuitBreaker()],
'symfony' => [$this->createSymfonyCircuitBreaker()],
];
}

/**
* @return SimpleCircuitBreaker the circuit breaker for testing purposes
*/
private function createSimpleCircuitBreaker()
private function createSimpleCircuitBreaker(): SimpleCircuitBreaker
{
return new SimpleCircuitBreaker(
new OpenPlace(0, 0, 1), // threshold 1s
new HalfOpenPlace(0, 0.2, 0), // timeout 0.2s to test the service
new ClosedPlace(2, 0.2, 0), // 2 failures allowed, 0.2s timeout
$this->getTestClient()
);
return new SimpleCircuitBreaker($this->getSystem(), $this->getTestClient());
}

/**
* @return SymfonyCircuitBreaker the circuit breaker for testing purposes
*/
private function createSymfonyCircuitBreaker()
private function createSymfonyCircuitBreaker(): SymfonyCircuitBreaker
{
$system = new MainSystem(
new ClosedPlace(2, 0.2, 0),
new HalfOpenPlace(0, 0.2, 0),
new OpenPlace(0, 0, 1)
);

$symfonyCache = new SymfonyCache(new ArrayCache());
$eventDispatcherS = $this->createMock(EventDispatcher::class);
$eventDispatcherS->expects($this->any())
->method('dispatch')
->willReturn($this->createMock(Event::class))
;

return new SymfonyCircuitBreaker(
$system,
$this->getSystem(),
$this->getTestClient(),
$symfonyCache,
$eventDispatcherS
Expand All @@ -177,7 +173,7 @@ private function createSymfonyCircuitBreaker()
/**
* @return callable the fallback callable
*/
private function createFallbackResponse()
private function createFallbackResponse(): callable
{
return function () {
return '{}';
Expand Down
6 changes: 3 additions & 3 deletions tests/SimpleCircuitBreakerFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class SimpleCircuitBreakerFactoryTest extends TestCase
/**
* @return void
*/
public function testCreation()
public function testCreation(): void
{
$factory = new SimpleCircuitBreakerFactory();

Expand All @@ -26,7 +26,7 @@ public function testCreation()
*
* @return void
*/
public function testCircuitBreakerCreation(array $settings)
public function testCircuitBreakerCreation(array $settings): void
{
$factory = new SimpleCircuitBreakerFactory();
$circuitBreaker = $factory->create($settings);
Expand All @@ -37,7 +37,7 @@ public function testCircuitBreakerCreation(array $settings)
/**
* @return array
*/
public function getSettings()
public function getSettings(): array
{
return [
[
Expand Down
Loading

0 comments on commit e5c315f

Please sign in to comment.