PHP code example of harris21 / laravel-fuse

1. Go to this page and download the library: Download harris21/laravel-fuse 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/ */

    

harris21 / laravel-fuse example snippets


use Harris21\Fuse\Middleware\CircuitBreakerMiddleware;

class ChargeCustomer implements ShouldQueue
{
    public $tries = 0;           // Unlimited releases
    public $maxExceptions = 3;   // Unhandled exceptions before the job fails

    public function middleware(): array
    {
        return [new CircuitBreakerMiddleware('stripe', release: 20)];
    }

    public function handle(): void
    {
        // Your payment logic - unchanged
        Stripe::charges()->create([...]);
    }
}

use Harris21\Fuse\Attributes\UseCircuitBreaker;
use Harris21\Fuse\Middleware\ResolvesCircuitBreakers;

#[UseCircuitBreaker('stripe')]
class ChargeCustomer implements ShouldQueue
{
    public function middleware(): array
    {
        return ResolvesCircuitBreakers::resolve($this);
    }
}

#[UseCircuitBreaker('stripe')]
#[UseCircuitBreaker('mailgun', release: 30)]
class ChargeAndNotify implements ShouldQueue
{
    public function middleware(): array
    {
        return ResolvesCircuitBreakers::resolve($this);
    }
}

#[UseCircuitBreaker(service: 'reports', window: 600)]

public function middleware(): array
{
    return ResolvesCircuitBreakers::merge($this, [
        new RateLimited('payments'),
    ]);
}

// config/fuse.php

return [
    'enabled' => env('FUSE_ENABLED', true),

    'default_threshold' => 50,      // Failure rate percentage to trip circuit
    'default_timeout' => 60,        // Seconds before testing recovery
    'default_min_requests' => 10,   // Minimum requests before evaluating
    'default_release' => 10,        // Seconds to delay an open-circuit job
    'default_probe_lock_ttl' => 65, // Seconds to hold the half-open probe lock
    'default_window' => 60,         // Seconds per failure-tracking window

    'services' => [
        'stripe' => [
            'threshold' => 50,
            'timeout' => 30,
            'min_requests' => 5,
            'release' => 15,

            // Peak hours: more tolerant during business hours
            'peak_hours_threshold' => 60,
            'peak_hours_start' => 9,   // 9 AM
            'peak_hours_end' => 17,    // 5 PM
        ],
        'mailgun' => [
            'threshold' => 60,
            'timeout' => 120,
            'min_requests' => 10,
            'window' => 300,        // 5-minute window for this lower-traffic service
        ],
    ],

    // Cache prefix — change if multiple apps share the same Redis instance
    'cache' => [
        'prefix' => env('FUSE_CACHE_PREFIX', 'fuse'),
    ],
];

'stripe' => [
    'threshold' => 40,              // Off-peak: more sensitive (40%)
    'peak_hours_threshold' => 60,   // Peak hours: more tolerant (60%)
    'peak_hours_start' => 9,        // 9 AM
    'peak_hours_end' => 17,         // 5 PM
],

'reports' => [
    'min_requests' => 10,
    'window' => 600,   // 10 minutes — long enough to gather 10 samples
],

// config/fuse.php

'services' => [
    'stripe' => [
        'threshold' => 50,
        'timeout' => 30,
        'min_requests' => 5,
        'failure_classifier' => \App\Fuse\StripeFailureClassifier::class,
    ],
],

namespace App\Fuse;

use GuzzleHttp\Exception\ServerException;
use Harris21\Fuse\Classifiers\DefaultFailureClassifier;
use Throwable;

class StripeFailureClassifier extends DefaultFailureClassifier
{
    public function shouldCount(Throwable $e): bool
    {
        // Stripe returns 500 for idempotency errors — not a real outage
        if ($e instanceof ServerException) {
            $body = (string) $e->getResponse()?->getBody();

            if (str_contains($body, 'idempotency')) {
                return false;
            }
        }

        return parent::shouldCount($e);
    }
}

namespace App\Fuse;

use Harris21\Fuse\Contracts\FailureClassifier;
use Throwable;

class CustomFailureClassifier implements FailureClassifier
{
    public function shouldCount(Throwable $e): bool
    {
        // Your classification logic
    }
}

namespace App\Jobs\Middleware;

use Illuminate\Http\Client\RequestException;

class ReleasesRateLimitedJobs
{
    public function handle(mixed $job, callable $next): mixed
    {
        try {
            return $next($job);
        } catch (RequestException $e) {
            if ($e->response->status() === 429) {
                $delay = (int) $e->response->header('Retry-After') ?: 30;

                return $job->release($delay);
            }

            throw $e;
        }
    }
}

public function middleware(): array
{
    return [
        new ReleasesRateLimitedJobs,
        new CircuitBreakerMiddleware('stripe'),
    ];
}

namespace App\Fuse;

use Harris21\Fuse\CircuitBreaker;
use Harris21\Fuse\Contracts\RecoveryStrategy;

class CustomRecoveryStrategy implements RecoveryStrategy
{
    public function allowsAttempt(CircuitBreaker $breaker): bool
    {
        // Return true to let this half-open job through, false to release it for a later retry.
    }

    public function recordSuccess(CircuitBreaker $breaker): bool
    {
        // Return true to fully close the circuit, false to keep testing.
    }

    public function recordFailure(CircuitBreaker $breaker): void
    {
        // Called when a half-open job fails. The circuit reopens around this call.
    }
}

'services' => [
    'stripe' => [
        'recovery_strategy' => \App\Fuse\CustomRecoveryStrategy::class,
    ],
],

'services' => [
    'stripe' => [
        'recovery_strategy' => \Harris21\Fuse\Strategies\OldestJobProbe::class,
    ],
],

use Harris21\Fuse\Events\CircuitBreakerOpened;
use Harris21\Fuse\Events\CircuitBreakerHalfOpen;
use Harris21\Fuse\Events\CircuitBreakerClosed;

// app/Listeners/AlertOnCircuitOpen.php

class AlertOnCircuitOpen
{
    public function handle(CircuitBreakerOpened $event): void
    {
        Log::critical("Circuit breaker opened for {$event->service}", [
            'failure_rate' => $event->failureRate,
            'attempts' => $event->attempts,
            'failures' => $event->failures,
        ]);

        // Send Slack notification, page on-call, etc.
    }
}

class NotifySlackOnCircuitOpen
{
    public function handle(CircuitBreakerOpened $event): void
    {
        Http::post(config('services.slack.webhook_url'), [
            'text' => sprintf(
                ':rotating_light: Circuit breaker OPEN for *%s* — %.1f%% failure rate (%d/%d requests)',
                $event->service,
                $event->failureRate,
                $event->failures,
                $event->attempts,
            ),
        ]);
    }
}

class RecordCircuitBreakerMetrics
{
    public function __construct(private CollectorRegistry $registry) {}

    private function gauge(): \Prometheus\Gauge
    {
        return $this->registry->getOrRegisterGauge(
            'fuse', 'circuit_open', 'Circuit breaker state (1 = open, 0 = closed)', ['service']
        );
    }

    public function handleOpened(CircuitBreakerOpened $event): void
    {
        $this->gauge()->set(1, [$event->service]);
    }

    public function handleClosed(CircuitBreakerClosed $event): void
    {
        $this->gauge()->set(0, [$event->service]);
    }
}

namespace App\Recorders;

use Harris21\Fuse\Events\CircuitBreakerOpened;
use Laravel\Pulse\Facades\Pulse;

class CircuitBreakerRecorder
{
    public array $listen = [CircuitBreakerOpened::class];

    public function record(CircuitBreakerOpened $event): void
    {
        Pulse::record('fuse_circuit_open', $event->service)->count();
    }
}

// config/fuse.php
'services' => [
    'stripe' => [
        'threshold' => 50,     // trip at 50% failure rate
        'timeout' => 30,       // wait 30s before probing again
        'min_requests' => 5,   // need at least 5 requests to evaluate
    ],
],

class ChargeCustomer implements ShouldQueue
{
    public $tries = 0;
    public $maxExceptions = 3;

    public function middleware(): array
    {
        return [new CircuitBreakerMiddleware('stripe', release: 20)];
    }

    public function handle(): void
    {
        Stripe::charges()->create([...]);
    }
}

class DeliverWebhook implements ShouldQueue
{
    public $tries = 0;
    public $maxExceptions = 3;

    public function __construct(
        private WebhookEndpoint $endpoint,
        private array $payload,
    ) {}

    public function middleware(): array
    {
        return [new CircuitBreakerMiddleware("webhook:{$this->endpoint->id}", release: 30)];
    }

    public function handle(): void
    {
        Http::timeout(5)->post($this->endpoint->url, $this->payload)->throw();
    }
}

use Illuminate\Support\Facades\Gate;

Gate::define('viewFuse', function ($user = null) {
    return $user?->isAdmin();
});

// config/fuse.php

'status_page' => [
    'enabled' => env('FUSE_STATUS_PAGE_ENABLED', false),
    'prefix' => env('FUSE_STATUS_PAGE_PREFIX', 'fuse'),
    'middleware' => [],          // Replaces the viewFuse check (see Authorization)
    'polling_interval' => 2,    // Frontend refresh interval in seconds
],

use Harris21\Fuse\CircuitBreaker;

$breaker = new CircuitBreaker('stripe');

if (!$breaker->isOpen()) {
    try {
        $result = Stripe::charges()->create([...]);
        $breaker->recordSuccess();
        return $result;
    } catch (Exception $e) {
        $breaker->recordFailure($e);
        throw $e;
    }
} else {
    // Circuit is open - use fallback
    return $this->fallbackResponse();
}

$breaker = new CircuitBreaker('stripe');

$breaker->isClosed();    // Normal operations
$breaker->isOpen();      // Protected, failing fast
$breaker->isHalfOpen();  // Testing recovery

$breaker->getStats();    // Get full statistics
$breaker->reset();       // Manually reset to closed
bash
php artisan vendor:publish --tag=fuse-config
bash
php artisan fuse:open stripe
bash
php artisan fuse:close stripe