Files
michaelschiemer/src/Framework/Queue/Wrappers/EmailQueue.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

424 lines
12 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Queue\Wrappers;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Queue\Queue;
use App\Framework\Queue\RetryStrategyHelper;
use App\Framework\Queue\ValueObjects\JobMetadata;
use App\Framework\Queue\ValueObjects\JobPayload;
use App\Framework\Queue\ValueObjects\QueuePriority;
use App\Framework\Retry\RetryStrategy;
/**
* Email Queue Wrapper
*
* Specialized queue wrapper for email processing with type-safe operations
* Optimized for email delivery, notifications, marketing emails, and system emails
*/
final readonly class EmailQueue
{
public function __construct(
private Queue $queue
) {
}
/**
* Push any object as an email job
*
* @param object $email Any email object to be processed
* @param QueuePriority|null $priority Email priority (defaults to normal)
* @param Duration|null $delay Delay before processing (defaults to immediate)
* @param Duration|null $timeout Maximum processing time
* @param RetryStrategy|null $retryStrategy Custom retry strategy
*/
public function pushEmail(
object $email,
?QueuePriority $priority = null,
?Duration $delay = null,
?Duration $timeout = null,
?RetryStrategy $retryStrategy = null
): void {
$payload = JobPayload::create(
job: $email,
priority: $priority ?? QueuePriority::normal(),
delay: $delay ?? Duration::zero(),
timeout: $timeout ?? Duration::fromMinutes(5),
retryStrategy: $retryStrategy ?? RetryStrategyHelper::forEmails(),
metadata: JobMetadata::forEmail($email)
);
$this->queue->push($payload);
}
/**
* Push a transactional email (high priority, immediate delivery)
*/
public function pushTransactionalEmail(object $email): void
{
$this->pushEmail(
email: $email,
priority: QueuePriority::high(),
timeout: Duration::fromMinutes(2),
retryStrategy: RetryStrategyHelper::forEmails()
);
}
/**
* Push a notification email (normal priority, moderate retry)
*/
public function pushNotificationEmail(object $email): void
{
$this->pushEmail(
email: $email,
priority: QueuePriority::normal(),
timeout: Duration::fromMinutes(5),
retryStrategy: RetryStrategyHelper::forEmails()
);
}
/**
* Push a marketing email (low priority, aggressive retry)
*/
public function pushMarketingEmail(object $email): void
{
$this->pushEmail(
email: $email,
priority: QueuePriority::low(),
timeout: Duration::fromMinutes(10),
retryStrategy: RetryStrategyHelper::forEmails()
);
}
/**
* Push a system email (high priority for admin notifications)
*/
public function pushSystemEmail(object $email): void
{
$this->pushEmail(
email: $email,
priority: QueuePriority::high(),
timeout: Duration::fromMinutes(3),
retryStrategy: RetryStrategyHelper::forEmails()
);
}
/**
* Push a bulk email (background processing with batching)
*/
public function pushBulkEmail(object $email): void
{
$this->pushEmail(
email: $email,
priority: QueuePriority::low(),
timeout: Duration::fromMinutes(15),
retryStrategy: RetryStrategyHelper::forEmails()
);
}
/**
* Push a scheduled email (delayed delivery)
*/
public function pushScheduledEmail(object $email, Duration $delay): void
{
$this->pushEmail(
email: $email,
delay: $delay,
priority: QueuePriority::normal(),
timeout: Duration::fromMinutes(5),
retryStrategy: RetryStrategyHelper::forEmails()
);
}
/**
* Push a critical email (immediate processing, no delays)
*/
public function pushCriticalEmail(object $email): void
{
$this->pushEmail(
email: $email,
priority: QueuePriority::critical(),
timeout: Duration::fromMinutes(1),
retryStrategy: RetryStrategyHelper::forEmails()
);
}
/**
* Push a reminder email (moderate priority, future delivery)
*/
public function pushReminderEmail(object $email, Duration $delay): void
{
$this->pushEmail(
email: $email,
delay: $delay,
priority: QueuePriority::normal(),
timeout: Duration::fromMinutes(3),
retryStrategy: RetryStrategyHelper::forEmails()
);
}
/**
* Pop the next email from the queue
*
* @return object|null The next email object or null if queue is empty
*/
public function popEmail(): ?object
{
$payload = $this->queue->pop();
return $payload?->job;
}
/**
* Peek at the next email without removing it
*
* @return object|null The next email object or null if queue is empty
*/
public function peekEmail(): ?object
{
$payload = $this->queue->peek();
return $payload?->job;
}
/**
* Pop the next email with its metadata
*
* @return JobPayload|null The complete job payload or null if queue is empty
*/
public function popEmailWithMetadata(): ?JobPayload
{
return $this->queue->pop();
}
/**
* Get number of pending emails
*/
public function size(): int
{
return $this->queue->size();
}
/**
* Check if queue is empty
*/
public function isEmpty(): bool
{
return $this->size() === 0;
}
/**
* Clear all emails from queue
*
* @return int Number of emails removed
*/
public function clear(): int
{
return $this->queue->clear();
}
/**
* Get email queue statistics
*/
public function getStats(): array
{
$stats = $this->queue->getStats();
return [
'type' => 'email',
'size' => $this->size(),
'is_empty' => $this->isEmpty(),
...$stats,
];
}
/**
* Batch push multiple emails
*
* @param array<object> $emails Array of email objects
* @param QueuePriority|null $priority Priority for all emails
*/
public function pushBatch(array $emails, ?QueuePriority $priority = null): void
{
foreach ($emails as $email) {
$this->pushEmail($email, $priority);
}
}
/**
* Push emails with different priorities based on email type
*
* @param array<object> $emails Array of email objects
*/
public function pushSmartBatch(array $emails): void
{
foreach ($emails as $email) {
$this->pushEmailByType($email);
}
}
/**
* Pop multiple emails in batch
*
* @param int $count Maximum number of emails to pop
* @return array<object> Array of email objects
*/
public function popBatch(int $count): array
{
$emails = [];
for ($i = 0; $i < $count; $i++) {
$email = $this->popEmail();
if ($email === null) {
break;
}
$emails[] = $email;
}
return $emails;
}
/**
* Filter emails by type/class
*
* @param string $emailClass The email class to filter by
* @return array<object> Emails of the specified type
*/
public function getEmailsByType(string $emailClass): array
{
$emails = [];
$tempEmails = [];
// Pop all emails temporarily
while (! $this->isEmpty()) {
$payload = $this->popEmailWithMetadata();
if ($payload === null) {
break;
}
if ($payload->job instanceof $emailClass) {
$emails[] = $payload->job;
} else {
$tempEmails[] = $payload;
}
}
// Push back non-matching emails
foreach ($tempEmails as $payload) {
$this->queue->push($payload);
}
return $emails;
}
/**
* Get emails count by priority
*
* @return array<string, int> Priority name => count mapping
*/
public function getEmailCountByPriority(): array
{
$stats = $this->getStats();
return $stats['priority_breakdown'] ?? [];
}
/**
* Get emails scheduled for future delivery
*
* @return array<object> Emails with future delivery times
*/
public function getScheduledEmails(): array
{
$scheduledEmails = [];
$tempEmails = [];
// Pop all emails temporarily
while (! $this->isEmpty()) {
$payload = $this->popEmailWithMetadata();
if ($payload === null) {
break;
}
if ($payload->isDelayed()) {
$scheduledEmails[] = $payload->job;
}
$tempEmails[] = $payload;
}
// Push back all emails
foreach ($tempEmails as $payload) {
$this->queue->push($payload);
}
return $scheduledEmails;
}
/**
* Get count of emails by delivery status
*
* @return array<string, int> Status counts
*/
public function getDeliveryStatusCounts(): array
{
return [
'pending' => $this->size(),
'scheduled' => count($this->getScheduledEmails()),
'immediate' => $this->size() - count($this->getScheduledEmails()),
];
}
/**
* Bulk push emails with rate limiting awareness
*
* @param array<object> $emails Array of email objects
* @param Duration $interval Interval between email processing
*/
public function pushBulkWithRateLimit(array $emails, Duration $interval): void
{
$delay = Duration::zero();
foreach ($emails as $email) {
$this->pushEmail(
email: $email,
delay: $delay,
priority: QueuePriority::low(),
timeout: Duration::fromMinutes(10)
);
$delay = $delay->add($interval);
}
}
/**
* Push email with automatic priority based on email type
*/
private function pushEmailByType(object $email): void
{
$className = get_class($email);
// Automatic priority assignment based on email name patterns
if (str_contains($className, 'Transactional') || str_contains($className, 'Receipt') || str_contains($className, 'Invoice')) {
$this->pushTransactionalEmail($email);
} elseif (str_contains($className, 'Marketing') || str_contains($className, 'Newsletter') || str_contains($className, 'Campaign')) {
$this->pushMarketingEmail($email);
} elseif (str_contains($className, 'System') || str_contains($className, 'Admin') || str_contains($className, 'Alert')) {
$this->pushSystemEmail($email);
} elseif (str_contains($className, 'Bulk') || str_contains($className, 'Batch')) {
$this->pushBulkEmail($email);
} elseif (str_contains($className, 'Critical') || str_contains($className, 'Emergency')) {
$this->pushCriticalEmail($email);
} elseif (str_contains($className, 'Reminder') || str_contains($className, 'Follow')) {
$this->pushReminderEmail($email, Duration::fromHours(1)); // Default 1 hour delay
} else {
// Default to notification email
$this->pushNotificationEmail($email);
}
}
}