PHP code example of ahmednour / circuit-breaker

1. Go to this page and download the library: Download ahmednour/circuit-breaker library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

ahmednour / circuit-breaker example snippets


'providers' => [
    // ...
    AhmedNour\CircuitBreaker\Providers\CircuitBreakerServiceProvider::class,
],

return [

    /*
    |--------------------------------------------------------------------------
    | Default Circuit Breaker Policy
    |--------------------------------------------------------------------------
    | Default policy applied when a provider does not specify custom rules.
    */
    'default' => [
        'failure_threshold'      => (int) env('CIRCUIT_BREAKER_DEFAULT_THRESHOLD', 5),
        'recovery_timeout'       => (int) env('CIRCUIT_BREAKER_DEFAULT_RECOVERY_TIMEOUT', 60),
        'half_open_lock_timeout' => (int) env('CIRCUIT_BREAKER_DEFAULT_LOCK_TIMEOUT', 10),
    ],

    /*
    |--------------------------------------------------------------------------
    | Provider-Specific Policies
    |--------------------------------------------------------------------------
    | Define custom thresholds, recovery timeouts, and probe lock TTLs per service.
    */
    'providers' => [
        'twilio-sms' => [
            'failure_threshold'      => 5,
            'recovery_timeout'       => 300,
            'half_open_lock_timeout' => 10,
        ],

        'payment-gateway' => [
            'failure_threshold'      => 3,
            'recovery_timeout'       => 60,
            'half_open_lock_timeout' => 10,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Redis Connection & Key Prefixes
    |--------------------------------------------------------------------------
    */
    'redis' => [
        'connection'  => env('CIRCUIT_BREAKER_REDIS_CONNECTION', 'default'),
        'prefix'      => 'circuit:',
        'lock_suffix' => ':probe-lock',
    ],

    /*
    |--------------------------------------------------------------------------
    | Infrastructure Failure Handling
    |--------------------------------------------------------------------------
    | Strategy when Circuit Breaker storage (e.g. Redis) is unavailable:
    | - 'fail_open'   : Allow the operation to execute (default).
    | - 'fail_closed' : Reject the operation with CircuitInfrastructureException.
    | - 'fallback'    : Delegate to a configured InfrastructureFallback implementation.
    */
    'infrastructure_failure' => [
        'strategy' => env('CIRCUIT_BREAKER_INFRASTRUCTURE_FAILURE_STRATEGY', 'fail_open'),
        'fallback' => null, // e.g., App\Services\CircuitBreaker\DatabaseFallback::class
    ],

];

namespace App\Services;

use AhmedNour\CircuitBreaker\Application\CircuitBreaker;
use AhmedNour\CircuitBreaker\Domain\Exceptions\CircuitOpenException;
use AhmedNour\CircuitBreaker\Domain\Exceptions\CircuitBusyException;

class SmsService
{
    public function __construct(
        private readonly CircuitBreaker $circuitBreaker,
        private readonly TwilioClient $twilio,
    ) {}

    public function sendSms(string $to, string $message): string
    {
        try {
            return $this->circuitBreaker->execute('twilio-sms', function () use ($to, $message) {
                return $this->twilio->messages->create($to, [
                    'body' => $message,
                ]);
            });
        } catch (CircuitOpenException $e) {
            // Service is known to be down — fallback or notify user
            logger()->warning("Twilio circuit is OPEN. Failing fast.");
            throw new ServiceUnavailableException("SMS gateway is temporarily unavailable.");
        } catch (CircuitBusyException $e) {
            // Another worker is currently testing the connection
            logger()->info("Twilio circuit is HALF-OPEN probe busy.");
            throw new ServiceBusyException("SMS gateway is recovering, please retry shortly.");
        }
    }
}

namespace App\Jobs;

use AhmedNour\CircuitBreaker\Application\CircuitBreaker;
use AhmedNour\CircuitBreaker\Domain\Exceptions\CircuitBusyException;
use AhmedNour\CircuitBreaker\Domain\Exceptions\CircuitOpenException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class SendSmsNotificationJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 5;

    public function __construct(
        public readonly string $phone,
        public readonly string $message,
    ) {}

    public function handle(CircuitBreaker $circuitBreaker, TwilioClient $twilio): void
    {
        try {
            $circuitBreaker->execute('twilio-sms', function () use ($twilio) {
                $twilio->send($this->phone, $this->message);
            });
        } catch (CircuitOpenException) {
            // Circuit is OPEN: Delay release by recovery window (e.g., 300s)
            $this->release(300);
        } catch (CircuitBusyException) {
            // Circuit is HALF_OPEN and another worker is probing: retry in 10s
            $this->release(10);
        }
    }
}

use AhmedNour\CircuitBreaker\Domain\CircuitExecution;
use AhmedNour\CircuitBreaker\Domain\Contracts\InfrastructureFallback;
use Throwable;

class DatabaseCircuitFallback implements InfrastructureFallback
{
    public function execute(CircuitExecution $execution, Throwable $exception): mixed
    {
        // Custom recovery logic, logging, or fallback execution
        return ($execution->callback)();
    }
}

'infrastructure_failure' => [
    'strategy' => 'fallback',
    'fallback' => App\Services\DatabaseCircuitFallback::class,
],
bash
php artisan vendor:publish --tag=circuit-breaker-config
bash
php vendor/bin/phpunit