Files
michaelschiemer/src/Framework/Http/MiddlewareCircuitBreaker.php
Michael Schiemer 55a330b223 Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
2025-08-11 20:13:26 +02:00

144 lines
5.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Http;
use App\Framework\CircuitBreaker\CircuitBreaker;
use App\Framework\CircuitBreaker\CircuitBreakerConfig;
use App\Framework\CircuitBreaker\CircuitState;
use App\Framework\Core\ValueObjects\Duration;
use RuntimeException;
use Throwable;
/**
* Circuit breaker for middlewares to prevent repeatedly failing middlewares from being executed
*
* This class is a wrapper around the CircuitBreaker module that provides middleware-specific functionality.
*/
final class MiddlewareCircuitBreaker
{
/**
* Default configuration for middleware circuit breakers
*/
private CircuitBreakerConfig $defaultConfig;
/**
* @param CircuitBreaker $circuitBreaker The circuit breaker instance
* @param int $failureThreshold Number of failures before opening the circuit
* @param int $recoveryTimeoutSeconds Time in seconds to keep the circuit open
*/
public function __construct(
private readonly CircuitBreaker $circuitBreaker,
int $failureThreshold = 5,
int $recoveryTimeoutSeconds = 60
) {
$this->defaultConfig = new CircuitBreakerConfig(
failureThreshold: $failureThreshold,
recoveryTimeout: Duration::fromSeconds($recoveryTimeoutSeconds),
halfOpenMaxAttempts: 3,
successThreshold: 2
);
}
/**
* Check if a middleware is allowed to execute
*
* @param string $middlewareName The fully qualified class name of the middleware
* @return bool True if the middleware is allowed to execute, false otherwise
*/
public function isAllowed(string $middlewareName): bool
{
try {
// Use the CircuitBreaker to check if the middleware is allowed to execute
$this->circuitBreaker->check($this->getServiceName($middlewareName), $this->defaultConfig);
return true;
} catch (Throwable $e) {
// If the circuit is open, the check method will throw an exception
return false;
}
}
/**
* Record a successful middleware execution
*
* @param string $middlewareName The fully qualified class name of the middleware
*/
public function recordSuccess(string $middlewareName): void
{
// Delegate to the CircuitBreaker
$this->circuitBreaker->recordSuccess($this->getServiceName($middlewareName), $this->defaultConfig);
}
/**
* Record a middleware failure
*
* @param string $middlewareName The fully qualified class name of the middleware
* @param int|null $failureThreshold Custom failure threshold for this middleware
* @param int|null $openTime Custom open time for this middleware
*/
public function recordFailure(string $middlewareName, ?int $failureThreshold = null, ?int $openTime = null): void
{
// Create a custom config if custom thresholds are provided
$config = $this->defaultConfig;
if ($failureThreshold !== null || $openTime !== null) {
$config = new CircuitBreakerConfig(
failureThreshold: $failureThreshold ?? $this->defaultConfig->failureThreshold,
recoveryTimeout: $openTime ? Duration::fromSeconds($openTime) : $this->defaultConfig->recoveryTimeout,
halfOpenMaxAttempts: $this->defaultConfig->halfOpenMaxAttempts,
successThreshold: $this->defaultConfig->successThreshold
);
}
// Create a RuntimeException to pass to the CircuitBreaker
$exception = new RuntimeException("Middleware {$middlewareName} failed");
// Delegate to the CircuitBreaker
$this->circuitBreaker->recordFailure($this->getServiceName($middlewareName), $exception, $config);
}
/**
* Get the state of the circuit for a middleware
*
* @param string $middlewareName The fully qualified class name of the middleware
* @return CircuitState The current state of the circuit
*/
public function getState(string $middlewareName): CircuitState
{
return $this->circuitBreaker->getState($this->getServiceName($middlewareName), $this->defaultConfig);
}
/**
* Reset the circuit for a middleware
*
* @param string $middlewareName The fully qualified class name of the middleware
*/
public function reset(string $middlewareName): void
{
$this->circuitBreaker->reset($this->getServiceName($middlewareName));
}
/**
* Get metrics for a middleware
*
* @param string $middlewareName The fully qualified class name of the middleware
* @return array Metrics for the middleware
*/
public function getMetrics(string $middlewareName): array
{
return $this->circuitBreaker->getMetrics($this->getServiceName($middlewareName));
}
/**
* Convert a middleware name to a service name for the CircuitBreaker
*
* @param string $middlewareName The fully qualified class name of the middleware
* @return string The service name for the CircuitBreaker
*/
private function getServiceName(string $middlewareName): string
{
return 'middleware:' . $middlewareName;
}
}