Files
michaelschiemer/src/Framework/Queue/Services/DeadLetterManager.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

162 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Queue\Services;
use App\Framework\Queue\Contracts\DeadLetterQueueInterface;
use App\Framework\Queue\Entities\DeadLetterJob;
use App\Framework\Queue\Entities\JobIndexEntry;
use App\Framework\Queue\ProductionJobPersistenceLayer;
use App\Framework\Queue\ValueObjects\DeadLetterQueueName;
use App\Framework\Queue\ValueObjects\FailureReason;
use App\Framework\Queue\ValueObjects\QueueName;
/**
* Manages dead letter queue operations and job failure handling
*/
final readonly class DeadLetterManager
{
public function __construct(
private DeadLetterQueueInterface $deadLetterQueue,
private ProductionJobPersistenceLayer $persistenceLayer
) {
}
/**
* Move a failed job to the dead letter queue
*/
public function moveJobToDeadLetterQueue(
JobIndexEntry $failedJob,
FailureReason $failureReason,
?DeadLetterQueueName $deadLetterQueueName = null
): void {
// Use default dead letter queue name if not provided
$dlqName = $deadLetterQueueName ?? DeadLetterQueueName::forQueue(
QueueName::fromString($failedJob->queueType)
);
// Create dead letter job entry
$deadLetterJob = DeadLetterJob::fromFailedJob(
failedJob: $failedJob,
deadLetterQueueName: $dlqName,
failureReason: $failureReason
);
// Add to dead letter queue
$this->deadLetterQueue->addFailedJob($deadLetterJob);
// Remove from original job index
$this->persistenceLayer->deleteJob($failedJob->jobId);
}
/**
* Move a job to dead letter queue when max attempts are exceeded
*/
public function handleJobFailure(
string $jobId,
\Throwable $exception,
int $maxAttempts = 3
): bool {
$jobEntry = $this->persistenceLayer->getJobById($jobId);
if (! $jobEntry) {
return false;
}
// Check if max attempts exceeded
if ($jobEntry->attempts >= $maxAttempts) {
$failureReason = FailureReason::fromException($exception);
$this->moveJobToDeadLetterQueue(
failedJob: $jobEntry,
failureReason: $failureReason
);
return true;
}
return false;
}
/**
* Retry a job from dead letter queue
*/
public function retryJob(string $deadLetterJobId): bool
{
return $this->deadLetterQueue->retryJob($deadLetterJobId);
}
/**
* Get failed jobs for monitoring/admin interface
*/
public function getFailedJobs(
?QueueName $originalQueue = null,
int $limit = 100
): array {
if ($originalQueue) {
return $this->deadLetterQueue->getJobsByOriginalQueue($originalQueue, $limit);
}
// Get jobs from all dead letter queues
$allJobs = [];
$queues = $this->deadLetterQueue->getAvailableQueues();
foreach ($queues as $queueName) {
$jobs = $this->deadLetterQueue->getJobs($queueName, $limit);
$allJobs = array_merge($allJobs, $jobs);
}
// Sort by moved_to_dlq_at descending
usort($allJobs, fn ($a, $b) => strcmp($b->movedToDlqAt, $a->movedToDlqAt));
return array_slice($allJobs, 0, $limit);
}
/**
* Get dead letter queue statistics
*/
public function getStatistics(): array
{
$queues = $this->deadLetterQueue->getAvailableQueues();
$stats = [];
foreach ($queues as $queueName) {
$stats[$queueName->toString()] = $this->deadLetterQueue->getQueueStats($queueName);
}
return $stats;
}
/**
* Clear all jobs from a dead letter queue
*/
public function clearDeadLetterQueue(DeadLetterQueueName $queueName): int
{
return $this->deadLetterQueue->clearQueue($queueName);
}
/**
* Retry all jobs in a dead letter queue
*/
public function retryAllJobs(DeadLetterQueueName $queueName): int
{
return $this->deadLetterQueue->retryAllJobs($queueName);
}
/**
* Delete a specific job from dead letter queue
*/
public function deleteJob(string $deadLetterJobId): bool
{
return $this->deadLetterQueue->deleteJob($deadLetterJobId);
}
/**
* Get all available dead letter queues
*/
public function getAvailableQueues(): array
{
return $this->deadLetterQueue->getAvailableQueues();
}
}