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
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\ErrorBoundaries\CircuitBreaker;
|
||||
|
||||
use App\Framework\Core\ValueObjects\Timestamp;
|
||||
use App\Framework\ErrorBoundaries\BoundaryConfig;
|
||||
use App\Framework\Logging\Logger;
|
||||
use App\Framework\StateManagement\StateManager;
|
||||
|
||||
/**
|
||||
* Circuit breaker manager for error boundaries using generic state management
|
||||
*/
|
||||
final readonly class BoundaryCircuitBreakerManager
|
||||
{
|
||||
public function __construct(
|
||||
/**
|
||||
* @var StateManager<BoundaryCircuitBreakerState>
|
||||
*/
|
||||
private StateManager $stateManager,
|
||||
private ?Logger $logger = null,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if circuit is open for given boundary
|
||||
*/
|
||||
public function isCircuitOpen(string $boundaryName, BoundaryConfig $config): bool
|
||||
{
|
||||
$state = $this->getCircuitState($boundaryName);
|
||||
|
||||
if ($state->isOpen()) {
|
||||
// Check if circuit should transition to half-open
|
||||
if ($this->shouldTransitionToHalfOpen($state, $config)) {
|
||||
$newState = $state->transitionToHalfOpen();
|
||||
$this->stateManager->setState($boundaryName, $newState);
|
||||
$this->log('info', "Circuit transitioned to HALF_OPEN for boundary: {$boundaryName}");
|
||||
|
||||
return false; // Allow operations in half-open state
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a successful operation
|
||||
*/
|
||||
public function recordSuccess(string $boundaryName, BoundaryConfig $config): void
|
||||
{
|
||||
$newState = $this->stateManager->updateState(
|
||||
$boundaryName,
|
||||
function (?BoundaryCircuitBreakerState $currentState) use ($config, $boundaryName): BoundaryCircuitBreakerState {
|
||||
$state = $currentState ?? new BoundaryCircuitBreakerState();
|
||||
$newState = $state->recordSuccess();
|
||||
|
||||
// If in half-open state and enough successes, close the circuit
|
||||
if ($state->isHalfOpen() && $newState->meetsSuccessThreshold($config->successThreshold ?? 3)) {
|
||||
$newState = $newState->transitionToClosed();
|
||||
$this->log('info', "Circuit transitioned to CLOSED for boundary: {$boundaryName}");
|
||||
}
|
||||
|
||||
return $newState;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a failed operation
|
||||
*/
|
||||
public function recordFailure(string $boundaryName, BoundaryConfig $config): void
|
||||
{
|
||||
$newState = $this->stateManager->updateState(
|
||||
$boundaryName,
|
||||
function (?BoundaryCircuitBreakerState $currentState) use ($config, $boundaryName): BoundaryCircuitBreakerState {
|
||||
$state = $currentState ?? new BoundaryCircuitBreakerState();
|
||||
$newState = $state->recordFailure();
|
||||
|
||||
// Check if failure threshold is exceeded
|
||||
if ($newState->exceedsFailureThreshold($config->circuitBreakerThreshold)) {
|
||||
$newState = $newState->transitionToOpen();
|
||||
$this->log('warning', "Circuit transitioned to OPEN for boundary: {$boundaryName} after {$config->circuitBreakerThreshold} failures");
|
||||
}
|
||||
|
||||
// If in half-open state, any failure should open the circuit
|
||||
if ($state->isHalfOpen()) {
|
||||
$newState = $newState->transitionToOpen();
|
||||
$this->log('warning', "Circuit transitioned to OPEN for boundary: {$boundaryName} due to failure in HALF_OPEN state");
|
||||
}
|
||||
|
||||
return $newState;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current circuit state
|
||||
*/
|
||||
public function getCircuitState(string $boundaryName): BoundaryCircuitBreakerState
|
||||
{
|
||||
return $this->stateManager->getState($boundaryName) ?? new BoundaryCircuitBreakerState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset circuit breaker state
|
||||
*/
|
||||
public function resetCircuit(string $boundaryName): void
|
||||
{
|
||||
$newState = new BoundaryCircuitBreakerState(); // Default closed state
|
||||
$this->stateManager->setState($boundaryName, $newState);
|
||||
$this->log('info', "Circuit reset for boundary: {$boundaryName}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all circuit breaker states
|
||||
*/
|
||||
public function getAllCircuitStates(): array
|
||||
{
|
||||
$states = $this->stateManager->getAllStates();
|
||||
$result = [];
|
||||
|
||||
foreach ($states as $boundaryName => $state) {
|
||||
$result[$boundaryName] = $this->getCircuitHealth($boundaryName);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check circuit health for monitoring
|
||||
*/
|
||||
public function getCircuitHealth(string $boundaryName): array
|
||||
{
|
||||
$state = $this->getCircuitState($boundaryName);
|
||||
|
||||
return [
|
||||
'boundary_name' => $boundaryName,
|
||||
'state' => $state->state->value,
|
||||
'state_description' => $state->state->getDescription(),
|
||||
'failure_count' => $state->failureCount,
|
||||
'success_count' => $state->successCount,
|
||||
'last_failure_time' => $state->lastFailureTime?->toIsoString(),
|
||||
'opened_at' => $state->openedAt?->toIsoString(),
|
||||
'half_open_attempts' => $state->halfOpenAttempts,
|
||||
'is_healthy' => $state->isClosed(),
|
||||
'severity' => $state->state->getSeverity(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get state manager statistics
|
||||
*/
|
||||
public function getStatistics(): array
|
||||
{
|
||||
return $this->stateManager->getStatistics()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment half-open attempts
|
||||
*/
|
||||
public function incrementHalfOpenAttempts(string $boundaryName): void
|
||||
{
|
||||
$this->stateManager->updateState(
|
||||
$boundaryName,
|
||||
function (?BoundaryCircuitBreakerState $currentState): BoundaryCircuitBreakerState {
|
||||
$state = $currentState ?? new BoundaryCircuitBreakerState();
|
||||
|
||||
return $state->incrementHalfOpenAttempts();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function shouldTransitionToHalfOpen(BoundaryCircuitBreakerState $state, BoundaryConfig $config): bool
|
||||
{
|
||||
if (! $state->isOpen() || $state->openedAt === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$timeSinceOpened = Timestamp::now()->diff($state->openedAt);
|
||||
|
||||
return $timeSinceOpened->isGreaterThan($config->circuitBreakerTimeout);
|
||||
}
|
||||
|
||||
private function log(string $level, string $message, array $context = []): void
|
||||
{
|
||||
if ($this->logger === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$context['component'] = 'BoundaryCircuitBreakerManager';
|
||||
|
||||
match ($level) {
|
||||
'debug' => $this->logger->debug("[ErrorBoundary] {$message}", $context),
|
||||
'info' => $this->logger->info("[ErrorBoundary] {$message}", $context),
|
||||
'warning' => $this->logger->warning("[ErrorBoundary] {$message}", $context),
|
||||
'error' => $this->logger->error("[ErrorBoundary] {$message}", $context),
|
||||
default => $this->logger->info("[ErrorBoundary] {$message}", $context),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user