- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
144 lines
5.1 KiB
PHP
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;
|
|
}
|
|
}
|