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

Commit

Permalink
Extracted business logic into Circuit breaker places
Browse files Browse the repository at this point in the history
  • Loading branch information
mickaelandrieu committed Jul 13, 2019
1 parent a12357e commit 4faa39a
Show file tree
Hide file tree
Showing 36 changed files with 569 additions and 333 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"psalm": "@php ./vendor/bin/psalm --threads=8 --diff --diff-methods",
"cs-fix": "@php ./vendor/bin/php-cs-fixer fix",
"test": "@php ./vendor/bin/phpunit",
"phpqa": "@php ./vendor/bin/phpqa --report --tools phpcs:0,phpmd:0,phpcpd:0,phpmetrics,phploc,pdepend,security-checker:0,parallel-lint:0 --ignoredDirs vendor"
"phpqa": "@php ./vendor/bin/phpqa --report --tools phpcs:0,phpmd:0,phpcpd:0,phpmetrics,phploc,pdepend,security-checker:0,parallel-lint:0 --ignoredDirs tests,vendor"
},
"scripts-descriptions": {
"phpstan": "Execute PHPStan analysis",
Expand Down
4 changes: 3 additions & 1 deletion psalm.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
</errorLevel>
</MixedAssignment>

<!-- Injection using setter (while not recommended) is perfectly valid! -->
<PropertyNotSetInConstructor errorLevel="suppress" />

<!-- level 3 issues - slightly lazy code writing, but provably low false-negatives -->

<DeprecatedMethod errorLevel="info" />
Expand All @@ -61,7 +64,6 @@
<InvalidDocblock errorLevel="info" />
<MisplacedRequiredParam errorLevel="info" />

<PropertyNotSetInConstructor errorLevel="info" />
<MissingConstructor errorLevel="info" />
<MissingClosureParamType errorLevel="info" />
<MissingParamType errorLevel="info" />
Expand Down
17 changes: 12 additions & 5 deletions src/Clients/GuzzleClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

namespace Resiliency\Clients;

use Exception;
use GuzzleHttp\Client as OriginalGuzzleClient;
use Resiliency\Contracts\Client;
use Resiliency\Exceptions\UnavailableService;
use Resiliency\Contracts\Service;
use Resiliency\Contracts\Client;
use Resiliency\Contracts\Place;
use Exception;

/**
* Guzzle implementation of client.
Expand All @@ -26,14 +28,19 @@ public function __construct(array $mainOptions = [])
/**
* {@inheritdoc}
*/
public function request($resource, array $options): string
public function request(Service $service, Place $place): string
{
$options = [];
try {
$client = new OriginalGuzzleClient($this->mainOptions);
$method = $this->defineMethod($options);
$method = $this->defineMethod($service->getParameters());
$options['http_errors'] = true;
$options['connect_timeout'] = $place->getTimeout();
$options['timeout'] = $place->getTimeout();

$clientParameters = array_merge($service->getParameters(), $options);

return (string) $client->request($method, $resource, $options)->getBody();
return (string) $client->request($method, $service->getURI(), $clientParameters)->getBody();
} catch (Exception $exception) {
throw new UnavailableService($exception->getMessage());
}
Expand Down
58 changes: 30 additions & 28 deletions src/Contracts/CircuitBreaker.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,57 +10,59 @@
interface CircuitBreaker
{
/**
* @return string the circuit breaker state
* @return Place the circuit breaker state
*/
public function getState(): string;
public function getState(): Place;

/**
* The function that execute the service.
*
* @param string $service the service to call
* @param array $serviceParameters the service parameters
* @param callable $fallback if the service is unavailable, rely on the fallback
*
* @return string
* @return Storage the circuit breaker storage
*/
public function call(string $service, callable $fallback, array $serviceParameters = []): string;
public function getStorage(): Storage;

/**
* @return bool checks if the circuit breaker is open
* @return TransitionDispatcher the circuit breaker dispatcher
*/
public function isOpened(): bool;
public function getDispatcher(): TransitionDispatcher;

/**
* @return bool checks if the circuit breaker is half open
*/
public function isHalfOpened(): bool;

/**
* @return bool checks if the circuit breaker is closed
*/
public function isClosed(): bool;

/**
* @return bool checks if the circuit breaker is isolated
* The function that try to reach the uri.
*
* @param string $uri the uri to call
* @param array $uriParameters the uri parameters
* @param callable $fallback if the service is unavailable, rely on the fallback
*
* @throws Exception in case of failure, throws an exception
*
* @return string
*/
public function isIsolated(): bool;
public function call(string $uri, callable $fallback, array $uriParameters = []): string;

/**
* Manually open (and hold open) the Circuit Breaker
* This can be used for example to take it offline for maintenance.
*
* @param string $service the service to call
* @param string $serviceUri the service to call
*
* @return self
*/
public function isolate(string $service): self;
public function isolate(string $serviceUri): self;

/**
* Reset the breaker to closed state to start accepting actions again.
*
* @param string $service the service to call
* @param string $serviceUri the service to call
*
* @return self
*/
public function reset(string $serviceUri): self;

/**
* Update the circuit breaker state
*
* @param string $state the Place state
* @param Service $service the service
*
* @return self
*/
public function reset(string $service): self;
public function moveStateTo($state, Service $service): self;
}
10 changes: 7 additions & 3 deletions src/Contracts/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Resiliency\Contracts;

use Resiliency\Exceptions\UnavailableService;

/**
* In charge of calling the resource and return a response.
* Must throw UnavailableService exception if not reachable.
Expand All @@ -14,10 +16,12 @@ interface Client
const DEFAULT_METHOD = 'GET';

/**
* @param string $resource the URI of the service to be reached
* @param array $options the options if needed
* @param Service $service the Service
* @param Place $place the place
*
* @throws UnavailableService
*
* @return string
*/
public function request($resource, array $options): string;
public function request(Service $service, Place $place): string;
}
4 changes: 3 additions & 1 deletion src/Contracts/Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace Resiliency\Contracts;

use Throwable;

/**
* Define any Resiliency exception.
*/
interface Exception
interface Exception extends Throwable
{
}
28 changes: 24 additions & 4 deletions src/Contracts/Place.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
namespace Resiliency\Contracts;

/**
* A circuit breaker can be in 3 places:
* closed, half open or open. Each place have its
* own properties and behaviors.
* A circuit breaker can be in 4 places: closed, half open, open or isolated.
* Each place have its own properties and behaviors.
*/
interface Place
{
/**
* Return the current state of the Circuit Breaker.
* Return the state.
*
* @return string
*/
Expand All @@ -30,4 +29,25 @@ public function getThreshold(): float;
* @return float the allowed timeout
*/
public function getTimeout(): float;

/**
* The function that execute the service.
*
* @param Transaction $transaction the service transaction
* @param callable $fallback if the service is unavailable, rely on the fallback
*
* @throws Exception in case of failure, throws an exception
*
* @return string
*/
public function call(Transaction $transaction, callable $fallback): string;

/**
* Set the Circuit Breaker to the place.
*
* @param CircuitBreaker $circuitBreaker the circuit breaker
*
* @return self
*/
public function setCircuitBreaker(CircuitBreaker $circuitBreaker): self;
}
19 changes: 19 additions & 0 deletions src/Contracts/Service.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Resiliency\Contracts;

/**
* Describe what is a service for the Resiliency library.
*/
interface Service
{
/**
* @return string the URI we try to reach
*/
public function getURI(): string;

/**
* @return array the URI parameters
*/
public function getParameters(): array;
}
11 changes: 9 additions & 2 deletions src/Contracts/Transaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
interface Transaction
{
/**
* @return string the service name
* @return Service the service
*/
public function getService(): string;
public function getService(): Service;

/**
* @return int the number of failures to call the service
Expand All @@ -37,4 +37,11 @@ public function getThresholdDateTime(): DateTime;
* @return bool
*/
public function incrementFailures(): bool;

/**
* If the service is up again, reset the number of failures to 0.
*
* @return bool
*/
public function clearFailures(): bool;
}
5 changes: 2 additions & 3 deletions src/Contracts/TransitionDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ interface TransitionDispatcher
* Dispatch a Circuit Breaker transition.
*
* @param CircuitBreaker $circuitBreaker the Circuit Breaker
* @param Service $service the service called
* @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;
public function dispatch(CircuitBreaker $circuitBreaker, Service $service, string $transition): void;
}
27 changes: 7 additions & 20 deletions src/Events/TransitionEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Resiliency\Events;

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

Expand All @@ -18,26 +19,20 @@ class TransitionEvent extends Event
private $eventName;

/**
* @var string the Service URI
* @var Service the Service URI
*/
private $service;

/**
* @var array the Service parameters
*/
private $parameters;

/**
* @param CircuitBreaker $circuitBreaker the circuit breaker
* @param string $eventName the transition name
* @param string $service the Service URI
* @param array $parameters the Service parameters
* @param Service $service the Service
*/
public function __construct(CircuitBreaker $circuitBreaker, $eventName, $service, array $parameters)
public function __construct(CircuitBreaker $circuitBreaker, string $eventName, Service $service)
{
$this->circuitBreaker = $circuitBreaker;
$this->eventName = $eventName;
$this->service = $service;
$this->parameters = $parameters;
}

/**
Expand All @@ -57,18 +52,10 @@ public function getEvent(): string
}

/**
* @return string the Service URI
* @return Service the Service
*/
public function getService(): string
public function getService(): Service
{
return $this->service;
}

/**
* @return array the Service parameters
*/
public function getParameters(): array
{
return $this->parameters;
}
}
2 changes: 1 addition & 1 deletion src/Exceptions/InvalidTransaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ final class InvalidTransaction extends Exception implements ResiliencyException
public static function invalidParameters($service, $failures, $state, $threshold): self
{
$exceptionMessage = 'Invalid parameters for Transaction' . PHP_EOL .
ErrorFormatter::format('service', $service, 'isURI', 'an URI') .
ErrorFormatter::format('service', $service, 'isAService', 'an instance of Service') .
ErrorFormatter::format('failures', $failures, 'isPositiveInteger', 'a positive integer') .
ErrorFormatter::format('state', $state, 'isString', 'a string') .
ErrorFormatter::format('threshold', $threshold, 'isPositiveInteger', 'a positive integer');
Expand Down
Loading

0 comments on commit 4faa39a

Please sign in to comment.