Files
trafficpumper/app/Serp/Operations/CircuitBreaker.php
T

54 lines
1.9 KiB
PHP
Raw Normal View History

2026-08-29 15:53:53 +03:00
<?php
namespace App\Serp\Operations;
use App\Serp\Exceptions\ProviderAuthenticationException;
use App\Serp\Exceptions\ProviderConfigurationException;
use App\Serp\Exceptions\SerpProviderException;
use Illuminate\Support\Facades\Cache;
final class CircuitBreaker
{
public function __construct(private KillSwitch $kill, private OperationAuditLogger $audit) {}
public function assertClosed(): void
{
try {
if (Cache::get('serp:circuit:open')) {
throw new OperationBlockedException('circuit_open');
}
} catch (OperationBlockedException $e) {
throw $e;
} catch (\Throwable) {
throw new OperationBlockedException('circuit_cache_unavailable');
}
}
public function success(): void
{
Cache::forget('serp:circuit:failures');
Cache::forget('serp:circuit:open');
}
public function failure(SerpProviderException $e): void
{
if ($e instanceof ProviderAuthenticationException || $e instanceof ProviderConfigurationException) {
Cache::put('serp:circuit:open', true, now()->addMinutes((int) config('serp.circuit_cooldown_minutes')));
$this->kill->activate('provider_auth_configuration');
$this->audit->record('circuit_opened', ['error_category' => $e->safeCode()]);
return;
}$n = (int) Cache::increment('serp:circuit:failures');
Cache::put('serp:circuit:failures', $n, now()->addMinutes((int) config('serp.circuit_window_minutes')));
if ($n >= (int) config('serp.circuit_failure_threshold')) {
Cache::put('serp:circuit:open', true, now()->addMinutes((int) config('serp.circuit_cooldown_minutes')));
$this->audit->record('circuit_opened', ['error_category' => $e->safeCode()]);
}
}
public function open(): bool
{
return (bool) Cache::get('serp:circuit:open');
}
}