PHP Classes

How to Use a PHP Circuit Breaker Pattern Implementation to Execute Actions that May Fail Using the Package Alecto - Circuit Breaker Pattern All-in-One Implementation: Execute actions using the Circuit Breaker pattern

Recommend this page to a friend!
  Info   Documentation   View files Files   Install with Composer Install with Composer   Download Download   Reputation   Support forum   Blog    
Last Updated Ratings Unique User Downloads Download Rankings
2024-12-04 (7 days ago) RSS 2.0 feedNot enough user ratingsTotal: 5 This week: 5All time: 11,502 This week: 12Up
Version License PHP version Categories
alecto 1.0.0GNU General Publi...8.1Language, Security, Design Patterns, P...
Description 

Author

This package can execute actions using the Circuit Breaker pattern.

It provides a class that can call a given action callback function that may succeed or fail.

When the action execution fails, the class uses the Circuit Breaker design pattern to retry executing the same action after a given period.

The class may retry up to a given number of times while the circuit is closed.

A given fallback function is called when the action function call fails.

If the callback function fails more times, the class opens the circuit and only retries calling the action function after a given timeout.

If the action function execution succeeds a given number of consecutive times, the circuit may be closed again when it is open.

Picture of Carlos Artur Curvelo da Matos
  Performance   Level  
Name: Carlos Artur Curvelo da ... <contact>
Classes: 24 packages by
Country: Portugal Portugal
Innovation award
Innovation award
Nominee: 15x

Winner: 2x

Instructions

Requirements:

  • PHP 8.1 or higher
  • `ext-pcntl` extension
  • `ext-posix` extension

Documentation

Alecto - PHP Circuit Breaker Library

PHPUnit Tests PHP Composer PHP Lint

A robust PHP implementation of the Circuit Breaker pattern with type safety, metrics tracking, and configurable behavior.

In the depths of Greek mythology, Alecto, one of the three Erinyes or Furies, emerges as a formidable figure. These divine beings were born from the blood of the castrated Uranus, embodying the relentless pursuit of vengeance. Alecto, in particular, is known for her unwavering determination and relentless pursuit of justice.

Her story offers a powerful metaphor for a circuit breaker pattern in programming. Just as Alecto intervenes to stop a cycle of violence and retribution, a circuit breaker interrupts a process to prevent it from spiraling out of control. In the realm of software, this might mean stopping an infinite loop, halting a runaway process, or preventing a system from crashing.

By drawing inspiration from Alecto, we can design circuit breaker patterns that are:

  • Swift and decisive: Like the Erinyes, a circuit breaker must act quickly to prevent further damage.
  • Relentless: Once activated, a circuit breaker should remain in effect until the underlying issue is resolved.
  • Just: A circuit breaker should be used judiciously, only when necessary to protect the system.

? Features

  • Type-safe implementation using PHP 8.1+
  • Configurable failure and success thresholds
  • Operation timeout handling
  • Fallback mechanism support
  • Metrics tracking
  • State transition management
  • PSR-3 compatible logging support

? Installation

Install via Composer:

composer require cmatosbc/circuit-breaker

Requirements: - PHP 8.1 or higher - ext-pcntl extension - ext-posix extension

? Quick Start

use Circuit\CircuitBreaker;
use Circuit\Config\CircuitBreakerConfig;
use Circuit\Service\TimeoutExecutor;

// Create configuration
$config = new CircuitBreakerConfig(
    failureThreshold: 3,    // Open circuit after 3 failures
    successThreshold: 2,    // Close circuit after 2 successes
    resetTimeout: 10,       // Wait 10 seconds before attempting recovery
    operationTimeout: 5     // Timeout operations after 5 seconds
);

// Initialize circuit breaker
$circuitBreaker = new CircuitBreaker(
    config: $config,
    timeoutExecutor: new TimeoutExecutor()
);

// Use the circuit breaker
try {
    $result = $circuitBreaker->call(
        callback: function() {
            return someRiskyOperation();
        },
        fallback: function() {
            return "Fallback response";
        }
    );
} catch (\Exception $e) {
    // Handle exception
}

? Circuit States

The circuit breaker operates in three states:

CLOSED (Normal Operation)

  • All requests are allowed through
  • Failures are counted
  • When failures reach `failureThreshold`, transitions to OPEN

OPEN (Failure Prevention)

  • All requests are immediately rejected
  • After `resetTimeout` seconds, transitions to HALF-OPEN
  • Supports fallback responses during rejection

HALF-OPEN (Recovery Attempt)

  • Limited requests are allowed through
  • Successes are counted
  • After `successThreshold` successes, transitions to CLOSED
  • Any failure returns to OPEN state

? Metrics Tracking

The circuit breaker tracks key metrics:

$metrics = $circuitBreaker->getMetrics();

// Available metrics:
$metrics['successes'];   // Successful operations count
$metrics['failures'];    // Failed operations count
$metrics['rejections']; // Rejected operations count (when circuit is open)

? Real-World Examples

1. API Client Protection

class ApiClient
{
    public function __construct(
        private CircuitBreaker $circuitBreaker,
        private HttpClient $httpClient
    ) {}

    public function fetchUserData(int $userId): array
    {
        return $this->circuitBreaker->call(
            callback: function() use ($userId) {
                $response = $this->httpClient->get("/users/{$userId}");
                return $response->toArray();
            },
            fallback: function() use ($userId) {
                return $this->getCachedUserData($userId);
            }
        );
    }
}

2. Database Query Protection

class DatabaseRepository
{
    public function __construct(
        private CircuitBreaker $circuitBreaker,
        private PDO $pdo
    ) {}

    public function executeQuery(string $query, array $params = []): array
    {
        return $this->circuitBreaker->call(
            callback: function() use ($query, $params) {
                $stmt = $this->pdo->prepare($query);
                $stmt->execute($params);
                return $stmt->fetchAll(PDO::FETCH_ASSOC);
            },
            fallback: function() {
                return $this->getFromCache() ?? [];
            }
        );
    }
}

3. Microservice Communication

class PaymentService
{
    public function __construct(
        private CircuitBreaker $circuitBreaker,
        private PaymentGateway $gateway
    ) {}

    public function processPayment(Order $order): PaymentResult
    {
        return $this->circuitBreaker->call(
            callback: function() use ($order) {
                return $this->gateway->processPayment($order);
            },
            fallback: function() use ($order) {
                $this->queueForRetry($order);
                return new PaymentResult(status: 'QUEUED');
            }
        );
    }
}

? Framework Integration

Symfony Integration

// config/services.yaml
services:
    Circuit\Config\CircuitBreakerConfig:
        arguments:
            $failureThreshold: '%env(int:CIRCUIT_FAILURE_THRESHOLD)%'
            $successThreshold: '%env(int:CIRCUIT_SUCCESS_THRESHOLD)%'
            $resetTimeout: '%env(int:CIRCUIT_RESET_TIMEOUT)%'
            $operationTimeout: '%env(int:CIRCUIT_OPERATION_TIMEOUT)%'

    Circuit\Service\TimeoutExecutor: ~

    Circuit\CircuitBreaker:
        arguments:
            $config: '@Circuit\Config\CircuitBreakerConfig'
            $timeoutExecutor: '@Circuit\Service\TimeoutExecutor'
            $logger: '@logger'

    App\Service\ExternalApiClient:
        arguments:
            $circuitBreaker: '@Circuit\CircuitBreaker'

// src/Service/ExternalApiClient.php
namespace App\Service;

use Circuit\CircuitBreaker;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class ExternalApiClient
{
    public function __construct(
        private readonly CircuitBreaker $circuitBreaker,
        private readonly HttpClientInterface $client
    ) {}

    public function fetchData(): array
    {
        return $this->circuitBreaker->call(
            callback: function() {
                $response = $this->client->request('GET', 'https://api.example.com/data');
                return $response->toArray();
            },
            fallback: function() {
                return $this->getCachedData();
            }
        );
    }
}

Laravel Integration

// app/Providers/CircuitBreakerServiceProvider.php
namespace App\Providers;

use Circuit\CircuitBreaker;
use Circuit\Config\CircuitBreakerConfig;
use Circuit\Service\TimeoutExecutor;
use Illuminate\Support\ServiceProvider;

class CircuitBreakerServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(CircuitBreakerConfig::class, function ($app) {
            return new CircuitBreakerConfig(
                failureThreshold: config('services.circuit.failure_threshold', 3),
                successThreshold: config('services.circuit.success_threshold', 2),
                resetTimeout: config('services.circuit.reset_timeout', 30),
                operationTimeout: config('services.circuit.operation_timeout', 5)
            );
        });

        $this->app->singleton(TimeoutExecutor::class);

        $this->app->singleton(CircuitBreaker::class, function ($app) {
            return new CircuitBreaker(
                config: $app->make(CircuitBreakerConfig::class),
                timeoutExecutor: $app->make(TimeoutExecutor::class),
                logger: $app->make('log')
            );
        });
    }
}

// config/services.php
return [
    'circuit' => [
        'failure_threshold' => env('CIRCUIT_FAILURE_THRESHOLD', 3),
        'success_threshold' => env('CIRCUIT_SUCCESS_THRESHOLD', 2),
        'reset_timeout' => env('CIRCUIT_RESET_TIMEOUT', 30),
        'operation_timeout' => env('CIRCUIT_OPERATION_TIMEOUT', 5),
    ],
];

// app/Services/PaymentGateway.php
namespace App\Services;

use Circuit\CircuitBreaker;

class PaymentGateway
{
    public function __construct(
        private readonly CircuitBreaker $circuitBreaker
    ) {}

    public function processPayment(array $paymentData): array
    {
        return $this->circuitBreaker->call(
            callback: function() use ($paymentData) {
                return $this->gateway->charge($paymentData);
            },
            fallback: function() use ($paymentData) {
                $this->queuePaymentForRetry($paymentData);
                return ['status' => 'queued'];
            }
        );
    }
}

?? Advanced Configuration

Custom Timeout Handling

$config = new CircuitBreakerConfig(
    failureThreshold: 5,
    successThreshold: 3,
    resetTimeout: 30,
    operationTimeout: 2  // Short timeout for time-sensitive operations
);

Adding Logging

use Psr\Log\LoggerInterface;

$circuitBreaker = new CircuitBreaker(
    config: $config,
    timeoutExecutor: new TimeoutExecutor(),
    logger: $psrLogger
);

? Testing

Run the test suite:

composer test

The tests demonstrate: - State transition behavior - Failure counting - Success threshold management - Timeout handling - Metrics accuracy

? Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for new features
  4. Submit a pull request

? License

This library is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.


  Files folder image Files (17)  
File Role Description
Files folder image.github (1 directory)
Files folder imagesrc (1 file, 6 directories)
Files folder imagetests (1 file)
Accessible without login Plain text file .phpcs-cache Data Auxiliary data
Accessible without login Plain text file composer.json Data Auxiliary data
Accessible without login Plain text file LICENSE Lic. License text
Accessible without login Plain text file phpcs.xml Data Auxiliary data
Accessible without login Plain text file phpunit.xml Data Auxiliary data
Accessible without login Plain text file README.md Doc. Documentation

  Files folder image Files (17)  /  .github  
File Role Description
Files folder imageworkflows (3 files)

  Files folder image Files (17)  /  .github  /  workflows  
File Role Description
  Accessible without login Plain text file composer.yml Data Auxiliary data
  Accessible without login Plain text file lint.yml Data Auxiliary data
  Accessible without login Plain text file phpunit.yml Data Auxiliary data

  Files folder image Files (17)  /  src  
File Role Description
Files folder imageConfig (1 file)
Files folder imageContract (1 file)
Files folder imageEnum (1 file)
Files folder imageException (1 file)
Files folder imageMetrics (1 file)
Files folder imageService (1 file)
  Plain text file CircuitBreaker.php Class Class source

  Files folder image Files (17)  /  src  /  Config  
File Role Description
  Plain text file CircuitBreakerConfig.php Class Class source

  Files folder image Files (17)  /  src  /  Contract  
File Role Description
  Plain text file LoggerInterface.php Class Class source

  Files folder image Files (17)  /  src  /  Enum  
File Role Description
  Accessible without login Plain text file CircuitState.php Aux. Configuration script

  Files folder image Files (17)  /  src  /  Exception  
File Role Description
  Plain text file CircuitOpenException.php Class Class source

  Files folder image Files (17)  /  src  /  Metrics  
File Role Description
  Plain text file CircuitMetrics.php Class Class source

  Files folder image Files (17)  /  src  /  Service  
File Role Description
  Plain text file TimeoutExecutor.php Class Class source

  Files folder image Files (17)  /  tests  
File Role Description
  Plain text file CircuitBreakerTest.php Class Class source

The PHP Classes site has supported package installation using the Composer tool since 2013, as you may verify by reading this instructions page.
Install with Composer Install with Composer
 Version Control Unique User Downloads Download Rankings  
 100%
Total:5
This week:5
All time:11,502
This week:12Up