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:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -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),
};
}
}

View File

@@ -0,0 +1,196 @@
<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries\CircuitBreaker;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\StateManagement\SerializableState;
/**
* Value object representing circuit breaker state for error boundaries
*/
final readonly class BoundaryCircuitBreakerState implements SerializableState
{
public function __construct(
public int $failureCount = 0,
public int $successCount = 0,
public ?Timestamp $lastFailureTime = null,
public ?Timestamp $openedAt = null,
public BoundaryCircuitState $state = BoundaryCircuitState::CLOSED,
public int $halfOpenAttempts = 0,
) {
}
/**
* Check if circuit is open
*/
public function isOpen(): bool
{
return $this->state === BoundaryCircuitState::OPEN;
}
/**
* Check if circuit is closed
*/
public function isClosed(): bool
{
return $this->state === BoundaryCircuitState::CLOSED;
}
/**
* Check if circuit is half-open
*/
public function isHalfOpen(): bool
{
return $this->state === BoundaryCircuitState::HALF_OPEN;
}
/**
* Record a failure and return new state
*/
public function recordFailure(): self
{
return new self(
failureCount: $this->failureCount + 1,
successCount: $this->successCount,
lastFailureTime: Timestamp::now(),
openedAt: $this->openedAt,
state: $this->state,
halfOpenAttempts: $this->halfOpenAttempts,
);
}
/**
* Record a success and return new state
*/
public function recordSuccess(): self
{
return new self(
failureCount: 0, // Reset failure count on success
successCount: $this->successCount + 1,
lastFailureTime: $this->lastFailureTime,
openedAt: null, // Reset opened time
state: $this->state,
halfOpenAttempts: 0, // Reset half-open attempts
);
}
/**
* Transition to OPEN state
*/
public function transitionToOpen(): self
{
return new self(
failureCount: $this->failureCount,
successCount: $this->successCount,
lastFailureTime: $this->lastFailureTime,
openedAt: Timestamp::now(),
state: BoundaryCircuitState::OPEN,
halfOpenAttempts: 0,
);
}
/**
* Transition to HALF_OPEN state
*/
public function transitionToHalfOpen(): self
{
return new self(
failureCount: $this->failureCount,
successCount: $this->successCount,
lastFailureTime: $this->lastFailureTime,
openedAt: $this->openedAt,
state: BoundaryCircuitState::HALF_OPEN,
halfOpenAttempts: 0,
);
}
/**
* Transition to CLOSED state
*/
public function transitionToClosed(): self
{
return new self(
failureCount: 0, // Reset failure count
successCount: $this->successCount,
lastFailureTime: null, // Reset last failure time
openedAt: null, // Reset opened time
state: BoundaryCircuitState::CLOSED,
halfOpenAttempts: 0,
);
}
/**
* Increment half-open attempts
*/
public function incrementHalfOpenAttempts(): self
{
return new self(
failureCount: $this->failureCount,
successCount: $this->successCount,
lastFailureTime: $this->lastFailureTime,
openedAt: $this->openedAt,
state: $this->state,
halfOpenAttempts: $this->halfOpenAttempts + 1,
);
}
/**
* Check if failure threshold is exceeded
*/
public function exceedsFailureThreshold(int $threshold): bool
{
return $this->failureCount >= $threshold;
}
/**
* Check if success threshold is met for half-open state
*/
public function meetsSuccessThreshold(int $threshold): bool
{
return $this->successCount >= $threshold;
}
/**
* Check if half-open attempts exceed maximum
*/
public function exceedsHalfOpenAttempts(int $maxAttempts): bool
{
return $this->halfOpenAttempts >= $maxAttempts;
}
/**
* Convert to array for serialization
*/
public function toArray(): array
{
return [
'failure_count' => $this->failureCount,
'success_count' => $this->successCount,
'last_failure_time' => $this->lastFailureTime?->toFloat(),
'opened_at' => $this->openedAt?->toFloat(),
'state' => $this->state->value,
'half_open_attempts' => $this->halfOpenAttempts,
];
}
/**
* Create from array (deserialization)
*/
public static function fromArray(array $data): static
{
return new self(
failureCount: $data['failure_count'] ?? 0,
successCount: $data['success_count'] ?? 0,
lastFailureTime: isset($data['last_failure_time'])
? Timestamp::fromFloat($data['last_failure_time'])
: null,
openedAt: isset($data['opened_at'])
? Timestamp::fromFloat($data['opened_at'])
: null,
state: BoundaryCircuitState::from($data['state'] ?? 'closed'),
halfOpenAttempts: $data['half_open_attempts'] ?? 0,
);
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries\CircuitBreaker;
/**
* Circuit breaker states for error boundaries
*/
enum BoundaryCircuitState: string
{
case CLOSED = 'closed';
case OPEN = 'open';
case HALF_OPEN = 'half_open';
/**
* Get human-readable description
*/
public function getDescription(): string
{
return match ($this) {
self::CLOSED => 'Circuit is closed - operations are allowed',
self::OPEN => 'Circuit is open - operations are blocked',
self::HALF_OPEN => 'Circuit is half-open - limited operations are allowed for testing',
};
}
/**
* Check if operations are allowed in this state
*/
public function allowsOperations(): bool
{
return match ($this) {
self::CLOSED => true,
self::OPEN => false,
self::HALF_OPEN => true, // Limited operations allowed
};
}
/**
* Get the severity level of this state
*/
public function getSeverity(): string
{
return match ($this) {
self::CLOSED => 'info',
self::OPEN => 'error',
self::HALF_OPEN => 'warning',
};
}
}