- 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.
162 lines
4.5 KiB
PHP
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();
|
|
}
|
|
}
|