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,71 @@
<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries\Async;
use App\Framework\Exception\FrameworkException;
use Throwable;
/**
* Exception thrown when both async operation and fallback fail
*/
final class AsyncBoundaryFailedException extends FrameworkException
{
public function __construct(
string $message,
private readonly string $boundaryName,
private readonly Throwable $originalException,
private readonly Throwable $fallbackException,
int $code = 0,
?Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
}
/**
* Get the boundary name where failure occurred
*/
public function getBoundaryName(): string
{
return $this->boundaryName;
}
/**
* Get the original operation exception
*/
public function getOriginalException(): Throwable
{
return $this->originalException;
}
/**
* Get the fallback exception
*/
public function getFallbackException(): Throwable
{
return $this->fallbackException;
}
/**
* Get detailed error information
*/
public function getErrorDetails(): array
{
return [
'boundary_name' => $this->boundaryName,
'original_error' => [
'class' => $this->originalException::class,
'message' => $this->originalException->getMessage(),
'file' => $this->originalException->getFile(),
'line' => $this->originalException->getLine(),
],
'fallback_error' => [
'class' => $this->fallbackException::class,
'message' => $this->fallbackException->getMessage(),
'file' => $this->fallbackException->getFile(),
'line' => $this->fallbackException->getLine(),
],
];
}
}

View File

@@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries\Async;
use App\Framework\Core\ValueObjects\Percentage;
use Throwable;
/**
* Result object for async bulk operations
*/
final readonly class AsyncBulkResult
{
public function __construct(
public array $results,
public array $errors,
public int $totalOperations,
public int $successfulOperations,
) {
}
/**
* Get success rate as percentage
*/
public function getSuccessRate(): Percentage
{
if ($this->totalOperations === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->successfulOperations / $this->totalOperations);
}
/**
* Get error rate as percentage
*/
public function getErrorRate(): Percentage
{
if ($this->totalOperations === 0) {
return Percentage::fromFloat(0.0);
}
return Percentage::fromFloat($this->getErrorCount() / $this->totalOperations);
}
/**
* Get number of errors
*/
public function getErrorCount(): int
{
return count($this->errors);
}
/**
* Check if any operations succeeded
*/
public function hasSuccesses(): bool
{
return $this->successfulOperations > 0;
}
/**
* Check if any operations failed
*/
public function hasErrors(): bool
{
return count($this->errors) > 0;
}
/**
* Check if all operations succeeded
*/
public function isCompleteSuccess(): bool
{
return $this->successfulOperations === $this->totalOperations;
}
/**
* Check if all operations failed
*/
public function isCompleteFailure(): bool
{
return count($this->errors) === $this->totalOperations;
}
/**
* Get all successful results
*/
public function getSuccessfulResults(): array
{
return $this->results;
}
/**
* Get all errors
*/
public function getErrors(): array
{
return $this->errors;
}
/**
* Get error by key
*/
public function getError(string $key): ?Throwable
{
return $this->errors[$key] ?? null;
}
/**
* Get result by key
*/
public function getResult(string $key): mixed
{
return $this->results[$key] ?? null;
}
/**
* Check if key has error
*/
public function hasError(string $key): bool
{
return isset($this->errors[$key]);
}
/**
* Check if key has result
*/
public function hasResult(string $key): bool
{
return isset($this->results[$key]);
}
/**
* Get summary statistics
*/
public function getSummary(): array
{
return [
'total_operations' => $this->totalOperations,
'successful_operations' => $this->successfulOperations,
'failed_operations' => $this->getErrorCount(),
'success_rate' => $this->getSuccessRate()->toFloat(),
'error_rate' => $this->getErrorRate()->toFloat(),
'is_complete_success' => $this->isCompleteSuccess(),
'is_complete_failure' => $this->isCompleteFailure(),
'has_partial_success' => $this->hasSuccesses() && $this->hasErrors(),
];
}
/**
* Convert to array
*/
public function toArray(): array
{
return [
'results' => $this->results,
'errors' => array_map(
fn (Throwable $e) => [
'class' => $e::class,
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
],
$this->errors
),
'summary' => $this->getSummary(),
];
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries\Async;
use App\Framework\Exception\FrameworkException;
/**
* Exception thrown when async circuit breaker is open
*/
final class AsyncCircuitBreakerOpenException extends FrameworkException
{
public function __construct(
string $message,
int $code = 0,
?\Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
}
}

View File

@@ -0,0 +1,382 @@
<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries\Async;
use App\Framework\Async\AsyncPromise;
use App\Framework\Async\FiberManager;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\Clock;
use App\Framework\DateTime\Timer;
use App\Framework\ErrorBoundaries\BoundaryConfig;
use App\Framework\ErrorBoundaries\CircuitBreaker\BoundaryCircuitBreakerManager;
use App\Framework\ErrorBoundaries\Events\BoundaryEventPublisher;
use App\Framework\ErrorBoundaries\Events\BoundaryExecutionFailed;
use App\Framework\ErrorBoundaries\Events\BoundaryExecutionSucceeded;
use App\Framework\ErrorBoundaries\Events\BoundaryFallbackExecuted;
use App\Framework\ErrorBoundaries\Events\BoundaryTimeoutOccurred;
use App\Framework\Logging\Logger;
use Throwable;
/**
* Async Error Boundary using framework's async components
*/
final readonly class AsyncErrorBoundary
{
public function __construct(
private string $boundaryName,
private BoundaryConfig $config,
private FiberManager $fiberManager,
private Clock $clock,
private Timer $timer,
private ?Logger $logger = null,
private ?BoundaryCircuitBreakerManager $circuitBreakerManager = null,
private ?BoundaryEventPublisher $eventPublisher = null,
) {
}
/**
* Execute async operation with error boundary
*
* @template T
* @param callable(): T $operation
* @param callable(): T|null $fallback
* @return AsyncPromise<T>
*/
public function executeAsync(callable $operation, ?callable $fallback = null): AsyncPromise
{
return AsyncPromise::create(function () use ($operation, $fallback) {
$startTime = $this->clock->time();
try {
$result = $this->executeWithRetryAsync($operation);
$executionTime = $startTime->age($this->clock);
// Publish success event
$this->publishEvent(new BoundaryExecutionSucceeded(
boundaryName: $this->boundaryName,
executionTime: $executionTime,
message: 'Async operation completed successfully',
));
return $result;
} catch (Throwable $e) {
$executionTime = $startTime->age($this->clock);
// Publish failure event
$this->publishEvent(new BoundaryExecutionFailed(
boundaryName: $this->boundaryName,
exception: $e,
executionTime: $executionTime,
willRetry: false,
message: 'Async operation failed',
));
return $this->handleFailureAsync($e, $fallback);
}
});
}
/**
* Execute multiple async operations concurrently with individual boundaries
*
* @param array<string, callable> $operations
* @param array<string, callable>|null $fallbacks
* @return AsyncPromise<array>
*/
public function executeConcurrent(array $operations, ?array $fallbacks = null): AsyncPromise
{
$promises = [];
foreach ($operations as $name => $operation) {
$fallback = $fallbacks[$name] ?? null;
$promises[$name] = $this->executeAsync($operation, $fallback);
}
return AsyncPromise::all($promises);
}
/**
* Execute with timeout using framework components
*
* @template T
* @param callable(): T $operation
* @param Duration $timeout
* @param callable(): T|null $fallback
* @return AsyncPromise<T>
*/
public function executeWithTimeout(
callable $operation,
Duration $timeout,
?callable $fallback = null
): AsyncPromise {
return AsyncPromise::create(function () use ($operation, $timeout, $fallback) {
try {
return $this->fiberManager->withTimeoutDuration($operation, $timeout);
} catch (Throwable $e) {
$this->publishEvent(new BoundaryTimeoutOccurred(
boundaryName: $this->boundaryName,
timeoutThreshold: $timeout,
actualExecutionTime: $timeout, // We don't know the exact time here
fallbackExecuted: $fallback !== null,
message: "Async operation timed out after {$timeout->toHumanReadable()}",
));
if ($fallback !== null) {
return $fallback();
}
throw $e;
}
});
}
/**
* Execute with circuit breaker support
*
* @template T
* @param callable(): T $operation
* @param callable(): T|null $fallback
* @return AsyncPromise<T>
*/
public function executeWithCircuitBreaker(callable $operation, ?callable $fallback = null): AsyncPromise
{
if ($this->config->circuitBreakerEnabled && $this->circuitBreakerManager) {
if ($this->circuitBreakerManager->isCircuitOpen($this->boundaryName, $this->config)) {
$this->log('info', 'Circuit breaker is open, using fallback');
if ($fallback !== null) {
return AsyncPromise::resolve($fallback());
}
return AsyncPromise::reject(new AsyncCircuitBreakerOpenException(
"Circuit breaker is open for boundary '{$this->boundaryName}'"
));
}
}
return AsyncPromise::create(function () use ($operation, $fallback) {
try {
$result = $operation();
if ($this->config->circuitBreakerEnabled && $this->circuitBreakerManager) {
$this->circuitBreakerManager->recordSuccess($this->boundaryName, $this->config);
}
return $result;
} catch (Throwable $e) {
if ($this->config->circuitBreakerEnabled && $this->circuitBreakerManager) {
$this->circuitBreakerManager->recordFailure($this->boundaryName, $this->config);
}
return $this->handleFailureAsync($e, $fallback);
}
});
}
/**
* Execute batch operations with individual error boundaries
*
* @template T
* @param array<T> $items
* @param callable(T): mixed $processor
* @param int $maxConcurrency
* @return AsyncPromise<AsyncBulkResult>
*/
public function executeBatch(
array $items,
callable $processor,
int $maxConcurrency = 10
): AsyncPromise {
return AsyncPromise::create(function () use ($items, $processor, $maxConcurrency) {
$operations = [];
foreach ($items as $key => $item) {
$operations[$key] = fn () => $processor($item);
}
$results = $this->fiberManager->throttled($operations, $maxConcurrency);
$successful = [];
$errors = [];
foreach ($results as $key => $result) {
if ($result instanceof Throwable) {
$errors[$key] = $result;
} else {
$successful[$key] = $result;
}
}
return new AsyncBulkResult(
results: $successful,
errors: $errors,
totalOperations: count($items),
successfulOperations: count($successful),
);
});
}
/**
* Execute operation with retry using framework components
*/
private function executeWithRetryAsync(callable $operation): mixed
{
$lastException = null;
for ($attempt = 1; $attempt <= $this->config->maxRetries; $attempt++) {
try {
return $operation();
} catch (Throwable $e) {
$lastException = $e;
// Don't retry certain types of errors
if (! $this->shouldRetry($e)) {
break;
}
if ($attempt < $this->config->maxRetries) {
$delay = $this->calculateRetryDelay($attempt);
$this->log('info', "Async attempt {$attempt} failed, retrying in {$delay}ms", [
'exception' => $e->getMessage(),
'attempt' => $attempt,
'delay' => $delay,
]);
// Use framework's timer for async sleep
$this->timer->sleep(Duration::fromMilliseconds($delay));
}
}
}
throw $lastException;
}
/**
* Handle failure with async fallback
*/
private function handleFailureAsync(Throwable $exception, ?callable $fallback): mixed
{
$this->logFailure($exception, 'Async operation failed');
if ($fallback === null) {
throw $exception;
}
try {
$result = $fallback();
// Publish fallback executed event
$this->publishEvent(new BoundaryFallbackExecuted(
boundaryName: $this->boundaryName,
originalException: $exception,
fallbackReason: 'Async operation failed: ' . $exception->getMessage(),
message: 'Async fallback executed successfully',
));
return $result;
} catch (Throwable $fallbackException) {
$this->log('error', 'Async fallback also failed', [
'original_exception' => $exception->getMessage(),
'fallback_exception' => $fallbackException->getMessage(),
]);
throw new AsyncBoundaryFailedException(
"Both async operation and fallback failed in boundary '{$this->boundaryName}'",
$this->boundaryName,
$exception,
$fallbackException
);
}
}
/**
* Check if exception should trigger retry
*/
private function shouldRetry(Throwable $e): bool
{
// Don't retry async-specific errors
if ($e instanceof AsyncCircuitBreakerOpenException) {
return false;
}
// Use same retry logic as synchronous boundary
return true; // Simplified for this example
}
/**
* Calculate retry delay
*/
private function calculateRetryDelay(int $attempt): int
{
$baseMs = (int) $this->config->baseDelay->toMilliseconds();
$maxMs = (int) $this->config->maxDelay->toMilliseconds();
return match ($this->config->retryStrategy) {
default => min($baseMs * $attempt, $maxMs),
};
}
/**
* Get async circuit breaker health
*/
public function getCircuitHealthAsync(): AsyncPromise
{
return AsyncPromise::create(function () {
if (! $this->config->circuitBreakerEnabled || ! $this->circuitBreakerManager) {
return null;
}
return $this->circuitBreakerManager->getCircuitHealth($this->boundaryName);
});
}
/**
* Reset circuit breaker asynchronously
*/
public function resetCircuitAsync(): AsyncPromise
{
return AsyncPromise::create(function () {
if ($this->config->circuitBreakerEnabled && $this->circuitBreakerManager) {
$this->circuitBreakerManager->resetCircuit($this->boundaryName);
}
});
}
private function logFailure(Throwable $e, string $message): void
{
$this->log('error', $message, [
'exception' => $e::class,
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
]);
}
private function log(string $level, string $message, array $context = []): void
{
if ($this->logger) {
$context['boundary'] = $this->boundaryName;
$context['async'] = true;
match ($level) {
'debug' => $this->logger->debug("[AsyncBoundary] {$message}", $context),
'info' => $this->logger->info("[AsyncBoundary] {$message}", $context),
'warning' => $this->logger->warning("[AsyncBoundary] {$message}", $context),
'error' => $this->logger->error("[AsyncBoundary] {$message}", $context),
default => $this->logger->info("[AsyncBoundary] {$message}", $context),
};
}
}
private function publishEvent($event): void
{
if ($this->eventPublisher !== null) {
$this->eventPublisher->publish($event);
}
}
}

View File

@@ -0,0 +1,128 @@
<?php
declare(strict_types=1);
namespace App\Framework\ErrorBoundaries\Async;
use App\Framework\Async\FiberManager;
use App\Framework\DateTime\Clock;
use App\Framework\DateTime\Timer;
use App\Framework\ErrorBoundaries\BoundaryConfig;
use App\Framework\ErrorBoundaries\CircuitBreaker\BoundaryCircuitBreakerManager;
use App\Framework\ErrorBoundaries\Events\BoundaryEventPublisher;
use App\Framework\EventBus\EventBus;
use App\Framework\Logging\Logger;
use App\Framework\StateManagement\StateManagerFactory;
/**
* Factory for creating async error boundaries
*/
final readonly class AsyncErrorBoundaryFactory
{
public function __construct(
private FiberManager $fiberManager,
private Clock $clock,
private Timer $timer,
private ?Logger $logger = null,
private ?StateManagerFactory $stateManagerFactory = null,
private ?EventBus $eventBus = null,
) {
}
/**
* Create async error boundary
*/
public function create(string $name, BoundaryConfig $config): AsyncErrorBoundary
{
$circuitBreakerManager = null;
if ($config->circuitBreakerEnabled && $this->stateManagerFactory) {
$stateManager = $this->stateManagerFactory->createForErrorBoundary();
$circuitBreakerManager = new BoundaryCircuitBreakerManager($stateManager, $this->logger);
}
$eventPublisher = new BoundaryEventPublisher($this->eventBus, $this->logger);
return new AsyncErrorBoundary(
boundaryName: $name,
config: $config,
fiberManager: $this->fiberManager,
clock: $this->clock,
timer: $this->timer,
logger: $this->logger,
circuitBreakerManager: $circuitBreakerManager,
eventPublisher: $eventPublisher,
);
}
/**
* Create async boundary for external service calls
*/
public function createForExternalService(string $serviceName): AsyncErrorBoundary
{
return $this->create("async_external_{$serviceName}", BoundaryConfig::externalService());
}
/**
* Create async boundary for database operations
*/
public function createForDatabase(string $operation = 'database'): AsyncErrorBoundary
{
return $this->create("async_{$operation}", BoundaryConfig::database());
}
/**
* Create async boundary for background jobs
*/
public function createForBackgroundJob(string $jobName): AsyncErrorBoundary
{
return $this->create("async_job_{$jobName}", BoundaryConfig::backgroundJob());
}
/**
* Create async boundary for critical operations
*/
public function createForCriticalOperation(string $operationName): AsyncErrorBoundary
{
return $this->create("async_critical_{$operationName}", BoundaryConfig::critical());
}
/**
* Create async boundary for batch processing
*/
public function createForBatchProcessing(string $batchName): AsyncErrorBoundary
{
$config = new BoundaryConfig(
maxRetries: 2,
retryStrategy: \App\Framework\ErrorBoundaries\RetryStrategy::EXPONENTIAL_JITTER,
baseDelay: \App\Framework\Core\ValueObjects\Duration::fromMilliseconds(100),
maxDelay: \App\Framework\Core\ValueObjects\Duration::fromSeconds(5),
circuitBreakerEnabled: true,
circuitBreakerThreshold: 10, // Higher threshold for batch operations
circuitBreakerTimeout: \App\Framework\Core\ValueObjects\Duration::fromMinutes(2),
enableMetrics: true,
maxBulkErrorRate: 0.2, // Allow 20% error rate in batch
);
return $this->create("async_batch_{$batchName}", $config);
}
/**
* Create async boundary for high-throughput operations
*/
public function createForHighThroughput(string $operationName): AsyncErrorBoundary
{
$config = new BoundaryConfig(
maxRetries: 1, // Fewer retries for high throughput
retryStrategy: \App\Framework\ErrorBoundaries\RetryStrategy::FIXED,
baseDelay: \App\Framework\Core\ValueObjects\Duration::fromMilliseconds(50),
maxDelay: \App\Framework\Core\ValueObjects\Duration::fromMilliseconds(200),
circuitBreakerEnabled: true,
circuitBreakerThreshold: 20, // Higher threshold
circuitBreakerTimeout: \App\Framework\Core\ValueObjects\Duration::fromSeconds(30),
enableMetrics: true,
);
return $this->create("async_throughput_{$operationName}", $config);
}
}

View File

@@ -0,0 +1,266 @@
# Async ErrorBoundary System
Complete async error boundary implementation using the framework's async components (`FiberManager`, `AsyncPromise`, etc.).
## Components
### Core Classes
- **`AsyncErrorBoundary`** - Main async error boundary using framework's `FiberManager` and `AsyncPromise`
- **`AsyncErrorBoundaryFactory`** - Factory for creating async boundaries with different configurations
- **`AsyncBulkResult`** - Result object for batch async operations with success/error tracking
- **`AsyncBoundaryFailedException`** - Exception for when both operation and fallback fail
- **`AsyncCircuitBreakerOpenException`** - Exception for circuit breaker open state
## Framework Integration
### Uses Framework Async Components
- **`FiberManager`** - Fiber-based async execution with batching and throttling
- **`AsyncPromise`** - Promise-based async/await pattern
- **`Clock`/`Timer`** - Time management using framework value objects
- **`Duration`/`Timestamp`** - Proper time value objects
### ErrorBoundary Integration
- Circuit breaker state management via `BoundaryCircuitBreakerManager`
- Event publishing via `BoundaryEventPublisher`
- Metrics and observability support
- Same retry strategies and configuration as sync boundaries
## Usage Examples
### Basic Async Operation
```php
$factory = new AsyncErrorBoundaryFactory($fiberManager, $clock, $timer);
$boundary = $factory->createForExternalService('payment_api');
$promise = $boundary->executeAsync(
operation: fn() => $paymentApi->processPayment($request),
fallback: fn() => $this->createOfflinePaymentRecord($request)
);
$result = $promise->await();
```
### Concurrent Operations
```php
$operations = [
'user_data' => fn() => $userService->getUserData($userId),
'preferences' => fn() => $preferenceService->getPreferences($userId),
'notifications' => fn() => $notificationService->getUnread($userId),
];
$promise = $boundary->executeConcurrent($operations);
$results = $promise->await();
// Results: ['user_data' => ..., 'preferences' => ..., 'notifications' => ...]
```
### Batch Processing
```php
$batchBoundary = $factory->createForBatchProcessing('email_sending');
$promise = $batchBoundary->executeBatch(
items: $emailQueue,
processor: fn($email) => $mailService->send($email),
maxConcurrency: 5
);
$bulkResult = $promise->await();
echo "Success rate: " . $bulkResult->getSuccessRate()->toFloat() * 100 . "%\n";
echo "Processed: {$bulkResult->successfulOperations}/{$bulkResult->totalOperations}\n";
```
### Timeout Handling
```php
$promise = $boundary->executeWithTimeout(
operation: fn() => $heavyComputation->process($data),
timeout: Duration::fromSeconds(30),
fallback: fn() => $this->getCachedResult($data)
);
try {
$result = $promise->await();
} catch (AsyncTimeoutException $e) {
// Handle timeout
}
```
### Circuit Breaker with Async
```php
$promise = $boundary->executeWithCircuitBreaker(
operation: fn() => $externalApi->call($request),
fallback: fn() => $this->getDefaultResponse()
);
$result = $promise->await();
```
## Factory Configurations
### External Service Boundary
```php
$boundary = $factory->createForExternalService('payment_service');
```
- Retry strategy with exponential backoff
- Circuit breaker enabled
- Appropriate timeouts for external calls
### Database Boundary
```php
$boundary = $factory->createForDatabase('user_queries');
```
- Fast fail for database issues
- Connection pooling aware
- Optimized for database operation patterns
### Background Job Boundary
```php
$boundary = $factory->createForBackgroundJob('email_processor');
```
- Higher retry counts
- Longer timeouts
- Batch error tolerance
### High Throughput Boundary
```php
$boundary = $factory->createForHighThroughput('api_endpoints');
```
- Minimal retries for speed
- Higher circuit breaker thresholds
- Optimized for high-volume operations
## Async Result Types
### AsyncBulkResult
```php
$bulkResult = $promise->await();
// Statistics
$successRate = $bulkResult->getSuccessRate(); // Percentage object
$errorRate = $bulkResult->getErrorRate(); // Percentage object
// Individual results
$userResult = $bulkResult->getResult('user_1');
$userError = $bulkResult->getError('user_1');
// Status checks
$hasAnyErrors = $bulkResult->hasErrors();
$isCompleteSuccess = $bulkResult->isCompleteSuccess();
```
## Event Integration
Async boundaries publish the same events as sync boundaries:
- `BoundaryExecutionSucceeded` - Successful async operation
- `BoundaryExecutionFailed` - Failed async operation
- `BoundaryFallbackExecuted` - Fallback was used
- `BoundaryTimeoutOccurred` - Operation timed out
## Promise Chaining
```php
$promise = $boundary->executeAsync(fn() => $service->getData())
->then(fn($data) => $this->processData($data))
->then(fn($processed) => $this->saveData($processed))
->catch(fn($error) => $this->handleError($error))
->finally(fn() => $this->cleanup());
$result = $promise->await();
```
## Concurrent with Fallbacks
```php
$operations = [
'primary_data' => fn() => $primaryService->getData(),
'backup_data' => fn() => $backupService->getData(),
];
$fallbacks = [
'primary_data' => fn() => $cache->get('primary_data'),
'backup_data' => fn() => $cache->get('backup_data'),
];
$promise = $boundary->executeConcurrent($operations, $fallbacks);
$results = $promise->await();
```
## Health Monitoring
```php
// Async health check
$healthPromise = $boundary->getCircuitHealthAsync();
$health = $healthPromise->await();
if ($health && !$health['is_healthy']) {
$this->alerting->sendAlert("Boundary {$boundary->boundaryName} is unhealthy");
}
// Async circuit reset
$resetPromise = $boundary->resetCircuitAsync();
$resetPromise->await();
```
## Performance Benefits
### Framework Integration Benefits
- **Fiber-based**: Uses PHP 8.1+ Fibers for true async execution
- **Batching**: Framework's `FiberManager` handles optimal batching
- **Throttling**: Built-in concurrency control
- **Timeout Management**: Framework's timeout handling with proper cleanup
### Error Boundary Benefits
- **Graceful Degradation**: Async fallbacks prevent service interruption
- **Circuit Breaking**: Prevents cascade failures in async operations
- **Retry Logic**: Async retry with proper delays
- **Observability**: Events and metrics for async operations
## Best Practices
### Concurrency Management
```php
// Good: Use factory methods for appropriate concurrency
$batchBoundary = $factory->createForBatchProcessing('data_sync');
$bulkResult = $batchBoundary->executeBatch($items, $processor, 10);
// Good: Throttle concurrent operations
$results = $fiberManager->throttled($operations, 5);
```
### Error Handling
```php
// Good: Provide meaningful fallbacks
$promise = $boundary->executeAsync(
operation: fn() => $externalService->getData(),
fallback: fn() => $this->getCachedData() // Always have a fallback
);
// Good: Handle different error types
try {
$result = $promise->await();
} catch (AsyncCircuitBreakerOpenException $e) {
// Circuit breaker is open
} catch (AsyncBoundaryFailedException $e) {
// Both operation and fallback failed
$originalError = $e->getOriginalException();
$fallbackError = $e->getFallbackException();
}
```
### Performance Optimization
```php
// Good: Use appropriate configurations
$highThroughputBoundary = $factory->createForHighThroughput('api_calls');
$batchBoundary = $factory->createForBatchProcessing('data_processing');
// Good: Monitor performance
$bulkResult = $batchBoundary->executeBatch($items, $processor);
$this->metrics->record('batch_success_rate', $bulkResult->getSuccessRate());
```