# Queue System

Comprehensive documentation of the asynchronous job processing system in the Custom PHP Framework.

## Overview

The Queue System provides robust, scalable background job processing with support for multiple queue drivers, priority-based execution, retry mechanisms, dead letter queues, and comprehensive monitoring.

**Core Features**:
- **Multi-Driver Support**: File-based, Redis, and Database-backed queues
- **Priority Queues**: Critical, high, normal, low priority levels
- **Delayed Jobs**: Schedule jobs for future execution
- **Retry Strategies**: Exponential backoff with configurable attempts
- **Dead Letter Queue**: Automatic failed job management
- **Job Persistence**: Full job lifecycle tracking and history
- **Progress Tracking**: Real-time job progress monitoring
- **Distributed Locking**: Multi-worker coordination
- **Metrics & Monitoring**: Performance statistics and health checks

## Queue Interface

The framework provides a unified `Queue` interface that all queue drivers implement:

```php
interface Queue
{
    /**
     * Push a job to the queue
     */
    public function push(JobPayload $payload): void;

    /**
     * Pop the next available job from the queue
     */
    public function pop(): ?JobPayload;

    /**
     * Peek at the next job without removing it
     */
    public function peek(): ?JobPayload;

    /**
     * Get the number of jobs in the queue
     */
    public function size(): int;

    /**
     * Clear all jobs from the queue
     */
    public function clear(): int;

    /**
     * Get queue statistics
     */
    public function getStats(): array;
}
```

### JobPayload Value Object

All jobs are wrapped in a `JobPayload` value object that provides type safety and configuration:

```php
final readonly class JobPayload
{
    public function __construct(
        public object $job,                    // The job instance
        public QueuePriority $priority,        // Priority level
        public Duration $delay,                // Execution delay
        public ?Duration $timeout = null,      // Max execution time
        public ?RetryStrategy $retryStrategy = null,  // Retry configuration
        public ?JobMetadata $metadata = null   // Additional metadata
    ) {}
}
```

**Factory Methods**:
```php
// Immediate execution with high priority
$payload = JobPayload::immediate($job);

// Delayed execution
$payload = JobPayload::delayed($job, Duration::fromMinutes(5));

// Critical priority with timeout
$payload = JobPayload::critical($job);

// Background job with retries
$payload = JobPayload::background($job);

// Custom configuration
$payload = JobPayload::create(
    job: $job,
    priority: QueuePriority::high(),
    delay: Duration::fromSeconds(30),
    timeout: Duration::fromMinutes(5),
    retryStrategy: new ExponentialBackoffStrategy(maxAttempts: 3)
);
```

## Queue Drivers

### File-Based Queue (Default)

Simple file-based queue implementation for development and small-scale applications:

**Features**:
- No external dependencies
- Persistent across restarts
- Suitable for development and small production loads
- Priority-based sorting

**Usage**:
```php
use App\Framework\Queue\FileQueue;

$queue = new FileQueue(
    directory: '/var/www/storage/queues',
    queueName: 'default'
);

$queue->push(JobPayload::immediate($job));
```

### Redis Queue (Production)

High-performance Redis-backed queue with advanced features:

**Features**:
- High throughput (10,000+ jobs/second)
- Redis sorted sets for priority queues
- Delayed job support via timestamp scoring
- Atomic operations for thread safety
- Real-time statistics

**Architecture**:
- Priority Queue: Redis sorted set with score = priority + timestamp
- Delayed Queue: Redis sorted set with score = execution timestamp
- Statistics: Redis hash with counters

**Usage**:
```php
use App\Framework\Queue\RedisQueue;

$queue = new RedisQueue(
    connection: $redisConnection,
    queueName: 'production',
    serializer: new PhpSerializer()
);

$queue->push(JobPayload::immediate($job));

// Statistics
$stats = $queue->getStats();
// [
//     'total_size' => 1523,
//     'priority_queue_size' => 1200,
//     'delayed_queue_size' => 323,
//     'priority_breakdown' => ['high' => 150, 'normal' => 1050, 'low' => 323]
// ]
```

**Score Calculation**:
```php
// Lower score = higher priority in Redis
$priorityScore = 1000 - $priority->value;  // Invert priority
$timeScore = $currentTime / 1000000;       // Microsecond precision for FIFO
$finalScore = $priorityScore + $timeScore;
```

**Delayed Job Processing**:
```php
// Automatic processing of ready delayed jobs
private function processDelayedJobs(): void
{
    $currentTime = time();

    // Get all delayed jobs where score <= current time
    $readyJobs = $this->redis->zRangeByScore($this->delayedKey, 0, $currentTime);

    foreach ($readyJobs as $serializedPayload) {
        // Remove from delayed queue
        $this->redis->zRem($this->delayedKey, $serializedPayload);

        // Add to priority queue
        $this->redis->zAdd($this->priorityKey, $score, $serializedPayload);
    }
}
```

### Persistent Queue Decorator

Wraps any queue implementation with job persistence and tracking:

```php
final readonly class PersistentQueue implements Queue
{
    public function __construct(
        private Queue $baseQueue,
        private JobPersistenceLayer $persistence,
        private QueueType $queueType
    ) {}

    public function push(JobPayload $payload): void
    {
        // Generate job ID
        $jobId = JobId::generate();

        // Store in persistence layer
        $this->persistence->storeJob(
            jobId: $jobId,
            queueType: $this->queueType,
            jobData: $payload->job,
            maxAttempts: $payload->retryStrategy->maxAttempts
        );

        // Push to base queue with job ID
        $this->baseQueue->push($payload->withJobId($jobId));
    }

    public function markJobCompleted(JobId $jobId, array $result = []): void
    {
        $this->persistence->markAsCompleted($jobId, $result);
    }

    public function markJobFailed(JobId $jobId, string $errorMessage): void
    {
        $jobState = $this->persistence->markAsFailed($jobId, $errorMessage);

        // Automatic retry if applicable
        if ($jobState->canRetry()) {
            $this->baseQueue->push($retryPayload);
            $this->persistence->markForRetry($jobId, $errorMessage);
        }
    }
}
```

## Background Jobs

### Creating a Job

Jobs are simple PHP classes with a `handle()` method:

```php
final readonly class SendWelcomeEmailJob
{
    public function __construct(
        private UserId $userId,
        private Email $email,
        private UserName $userName
    ) {}

    public function handle(): array
    {
        // Business logic
        $this->emailService->send(
            to: $this->email,
            template: 'welcome',
            data: ['name' => $this->userName->value]
        );

        // Return result for logging
        return [
            'user_id' => $this->userId->toString(),
            'email' => $this->email->getMasked(),
            'sent_at' => time()
        ];
    }

    public function getType(): string
    {
        return 'email.welcome';
    }
}
```

### Dispatching Jobs

```php
use App\Framework\Queue\Queue;
use App\Framework\Queue\ValueObjects\JobPayload;

// Immediate execution
$job = new SendWelcomeEmailJob($userId, $email, $userName);
$queue->push(JobPayload::immediate($job));

// Delayed execution (5 minutes)
$queue->push(JobPayload::delayed($job, Duration::fromMinutes(5)));

// Background job with retries
$queue->push(JobPayload::background($job));

// Custom configuration
$queue->push(JobPayload::create(
    job: $job,
    priority: QueuePriority::high(),
    timeout: Duration::fromMinutes(5),
    retryStrategy: new ExponentialBackoffStrategy(maxAttempts: 3)
));
```

### Processing Jobs

```php
// Worker loop
while (true) {
    $payload = $queue->pop();

    if ($payload === null) {
        sleep(1); // Wait for new jobs
        continue;
    }

    try {
        // Execute job
        $result = $payload->job->handle();

        // Mark as completed
        if ($queue instanceof PersistentQueue) {
            $queue->markJobCompleted($payload->getJobId(), $result);
        }
    } catch (\Throwable $e) {
        // Mark as failed
        if ($queue instanceof PersistentQueue) {
            $queue->markJobFailed(
                $payload->getJobId(),
                $e->getMessage(),
                $e
            );
        }
    }
}
```

## Priority System

### QueuePriority Value Object

```php
enum QueuePriority: int
{
    case CRITICAL = 1000;  // System-critical operations
    case HIGH = 750;       // User-facing operations
    case NORMAL = 500;     // Standard background tasks
    case LOW = 250;        // Non-urgent maintenance

    public static function critical(): self { return self::CRITICAL; }
    public static function high(): self { return self::HIGH; }
    public static function normal(): self { return self::NORMAL; }
    public static function low(): self { return self::LOW; }

    public function toString(): string
    {
        return match ($this) {
            self::CRITICAL => 'critical',
            self::HIGH => 'high',
            self::NORMAL => 'normal',
            self::LOW => 'low',
        };
    }
}
```

### Priority Usage Examples

```php
// Critical: Payment processing, real-time notifications
$queue->push(JobPayload::create(
    job: new ProcessPaymentJob($payment),
    priority: QueuePriority::critical(),
    timeout: Duration::fromSeconds(30)
));

// High: User-triggered emails, report generation
$queue->push(JobPayload::create(
    job: new GenerateReportJob($userId),
    priority: QueuePriority::high()
));

// Normal: Scheduled maintenance, cache warming
$queue->push(JobPayload::create(
    job: new WarmCacheJob(),
    priority: QueuePriority::normal()
));

// Low: Cleanup tasks, analytics aggregation
$queue->push(JobPayload::create(
    job: new CleanupOldLogsJob(),
    priority: QueuePriority::low()
));
```

## Retry Mechanisms

### RetryStrategy Interface

```php
interface RetryStrategy
{
    public function shouldRetry(int $attempts): bool;
    public function getDelay(int $attempts): Duration;
    public function getMaxAttempts(): int;
}
```

### Exponential Backoff Strategy

```php
final readonly class ExponentialBackoffStrategy implements RetryStrategy
{
    public function __construct(
        private int $maxAttempts = 3,
        private int $baseDelaySeconds = 60,
        private int $maxDelaySeconds = 3600
    ) {}

    public function shouldRetry(int $attempts): bool
    {
        return $attempts < $this->maxAttempts;
    }

    public function getDelay(int $attempts): Duration
    {
        // 2^attempts * baseDelay, capped at maxDelay
        $delaySeconds = min(
            pow(2, $attempts) * $this->baseDelaySeconds,
            $this->maxDelaySeconds
        );

        return Duration::fromSeconds($delaySeconds);
    }

    public function getMaxAttempts(): int
    {
        return $this->maxAttempts;
    }
}
```

### Custom Retry Strategy

```php
final readonly class CustomRetryStrategy implements RetryStrategy
{
    public function __construct(
        private array $retryDelays = [60, 300, 1800] // 1min, 5min, 30min
    ) {}

    public function shouldRetry(int $attempts): bool
    {
        return $attempts < count($this->retryDelays);
    }

    public function getDelay(int $attempts): Duration
    {
        $index = min($attempts, count($this->retryDelays) - 1);
        return Duration::fromSeconds($this->retryDelays[$index]);
    }

    public function getMaxAttempts(): int
    {
        return count($this->retryDelays);
    }
}
```

### Usage with Jobs

```php
// Standard exponential backoff
$payload = JobPayload::create(
    job: $job,
    retryStrategy: new ExponentialBackoffStrategy(
        maxAttempts: 5,
        baseDelaySeconds: 60,    // Start with 1 minute
        maxDelaySeconds: 3600    // Cap at 1 hour
    )
);

// Custom retry schedule
$payload = JobPayload::create(
    job: $job,
    retryStrategy: new CustomRetryStrategy([60, 300, 900, 3600])
);

// No retries
$payload = JobPayload::create(
    job: $job,
    retryStrategy: null  // Job fails permanently on first error
);
```

## Failed Jobs & Dead Letter Queue

### JobStatus Lifecycle

```php
enum JobStatus: string
{
    case PENDING = 'pending';       // Waiting in queue
    case PROCESSING = 'processing'; // Currently executing
    case COMPLETED = 'completed';   // Successfully completed
    case FAILED = 'failed';        // Failed with error
    case RETRYING = 'retrying';    // Failed, will retry
    case CANCELLED = 'cancelled';  // Manually cancelled
    case EXPIRED = 'expired';      // Timeout exceeded

    public function isFinal(): bool
    {
        return match ($this) {
            self::COMPLETED, self::CANCELLED, self::EXPIRED => true,
            default => false
        };
    }

    public function isActive(): bool
    {
        return match ($this) {
            self::PENDING, self::PROCESSING, self::RETRYING => true,
            default => false
        };
    }

    public function isFailure(): bool
    {
        return match ($this) {
            self::FAILED, self::EXPIRED => true,
            default => false
        };
    }
}
```

### Dead Letter Queue Manager

When jobs exceed max retry attempts, they are automatically moved to a dead letter queue:

```php
final readonly class DeadLetterManager
{
    public function __construct(
        private DeadLetterQueueInterface $deadLetterQueue,
        private ProductionJobPersistenceLayer $persistenceLayer
    ) {}

    /**
     * Move failed job to dead letter queue
     */
    public function moveJobToDeadLetterQueue(
        JobIndexEntry $failedJob,
        FailureReason $failureReason
    ): void {
        $dlqName = DeadLetterQueueName::forQueue(
            QueueName::fromString($failedJob->queueType)
        );

        $deadLetterJob = DeadLetterJob::fromFailedJob(
            failedJob: $failedJob,
            deadLetterQueueName: $dlqName,
            failureReason: $failureReason
        );

        $this->deadLetterQueue->addFailedJob($deadLetterJob);
        $this->persistenceLayer->deleteJob($failedJob->jobId);
    }

    /**
     * Handle job failure with automatic DLQ routing
     */
    public function handleJobFailure(
        string $jobId,
        \Throwable $exception,
        int $maxAttempts = 3
    ): bool {
        $jobEntry = $this->persistenceLayer->getJobById($jobId);

        if ($jobEntry->attempts >= $maxAttempts) {
            $this->moveJobToDeadLetterQueue(
                failedJob: $jobEntry,
                failureReason: FailureReason::fromException($exception)
            );

            return true;
        }

        return false;
    }
}
```

### Managing Failed Jobs

```php
use App\Framework\Queue\Services\DeadLetterManager;

// Get failed jobs
$failedJobs = $deadLetterManager->getFailedJobs(
    originalQueue: QueueName::fromString('emails'),
    limit: 100
);

// Retry a specific job
$deadLetterManager->retryJob($deadLetterJobId);

// Retry all jobs in a dead letter queue
$retriedCount = $deadLetterManager->retryAllJobs($deadLetterQueueName);

// Clear dead letter queue
$deletedCount = $deadLetterManager->clearDeadLetterQueue($deadLetterQueueName);

// Get statistics
$stats = $deadLetterManager->getStatistics();
// [
//     'default.dlq' => ['count' => 23, 'oldest' => '2024-01-15 10:30:00'],
//     'emails.dlq' => ['count' => 5, 'oldest' => '2024-01-16 14:20:00']
// ]
```

## Queue Monitoring

### Queue Statistics

```php
// Get comprehensive statistics
$stats = $queue->getStats();

// Example output:
[
    'total_size' => 1523,
    'priority_queue_size' => 1200,
    'delayed_queue_size' => 323,
    'priority_breakdown' => [
        'critical' => 15,
        'high' => 150,
        'normal' => 1050,
        'low' => 308
    ],
    'stats' => [
        'total_pushed' => 125430,
        'total_popped' => 123907,
        'high_pushed' => 15234,
        'normal_pushed' => 95678,
        'low_pushed' => 14518,
        'last_activity' => 1705410234
    ],
    'persistence' => [
        'total_jobs' => 125430,
        'completed' => 120145,
        'failed' => 3762,
        'retrying' => 1523
    ]
]
```

### Job Metrics

```php
use App\Framework\Queue\Services\JobMetricsManager;

// Get job performance metrics
$metrics = $metricsManager->getJobMetrics($jobId);

// [
//     'execution_time_ms' => 1234,
//     'memory_used_mb' => 45.2,
//     'peak_memory_mb' => 52.8,
//     'attempts' => 1,
//     'last_attempt_at' => '2024-01-16 15:30:00'
// ]

// Get aggregated metrics by job type
$aggregated = $metricsManager->getAggregatedMetrics('email.welcome');

// [
//     'total_executions' => 1523,
//     'avg_execution_time_ms' => 856,
//     'p95_execution_time_ms' => 1450,
//     'p99_execution_time_ms' => 2340,
//     'success_rate' => 0.987,
//     'avg_memory_mb' => 38.4
// ]
```

### Progress Tracking

For long-running jobs, track progress:

```php
final readonly class LargeDataImportJob
{
    public function __construct(
        private ProgressManager $progressManager,
        private JobId $jobId,
        private array $dataFiles
    ) {}

    public function handle(): array
    {
        $totalSteps = count($this->dataFiles);

        foreach ($this->dataFiles as $index => $file) {
            // Process file
            $this->processFile($file);

            // Update progress
            $this->progressManager->updateProgress(
                jobId: $this->jobId,
                currentStep: $index + 1,
                totalSteps: $totalSteps,
                message: "Processing {$file}"
            );
        }

        return ['files_processed' => $totalSteps];
    }
}

// Query progress
$progress = $progressManager->getProgress($jobId);

// [
//     'current_step' => 45,
//     'total_steps' => 100,
//     'percentage' => 45.0,
//     'message' => 'Processing data_2024_01_16.csv',
//     'updated_at' => '2024-01-16 15:32:45'
// ]
```

### Health Monitoring

```php
use App\Framework\Queue\Services\WorkerHealthCheckService;

// Check worker health
$health = $healthCheckService->checkAllWorkers();

// [
//     'healthy_workers' => 8,
//     'unhealthy_workers' => 1,
//     'total_workers' => 9,
//     'workers' => [
//         ['id' => 'worker-1', 'status' => 'healthy', 'last_heartbeat' => '2024-01-16 15:33:00'],
//         ['id' => 'worker-2', 'status' => 'unhealthy', 'last_heartbeat' => '2024-01-16 15:20:15']
//     ]
// ]

// Register worker heartbeat
$healthCheckService->registerHeartbeat($workerId);

// Cleanup stale workers
$cleanedCount = $healthCheckService->cleanupStaleWorkers(
    staleThreshold: Duration::fromMinutes(5)
);
```

## Best Practices

### 1. Job Design

**Keep Jobs Small and Focused**:
```php
// ✅ Good: Single responsibility
final readonly class SendEmailJob
{
    public function handle(): array
    {
        $this->emailService->send($this->email, $this->template);
        return ['sent' => true];
    }
}

// ❌ Bad: Multiple responsibilities
final readonly class SendEmailAndUpdateUserJob
{
    public function handle(): array
    {
        $this->emailService->send($this->email);
        $this->userRepository->update($this->userId);  // Separate concern
        $this->analyticsService->track($this->event);  // Separate concern
    }
}
```

**Make Jobs Idempotent**:
```php
final readonly class ProcessPaymentJob
{
    public function handle(): array
    {
        // Check if already processed (idempotency)
        if ($this->paymentRepository->isProcessed($this->paymentId)) {
            return ['status' => 'already_processed'];
        }

        // Process payment
        $result = $this->paymentGateway->charge($this->payment);

        // Mark as processed
        $this->paymentRepository->markAsProcessed($this->paymentId);

        return $result;
    }
}
```

**Use Value Objects for Job Data**:
```php
// ✅ Good: Type-safe value objects
final readonly class SendInvoiceJob
{
    public function __construct(
        private InvoiceId $invoiceId,
        private Email $recipientEmail,
        private Money $amount
    ) {}
}

// ❌ Bad: Primitive arrays
final readonly class SendInvoiceJob
{
    public function __construct(
        private array $data  // What structure? What validation?
    ) {}
}
```

### 2. Priority Assignment

**Critical**: System-critical operations that must execute immediately
- Payment processing
- Real-time notifications
- Security alerts

**High**: User-facing operations with user waiting
- Password reset emails
- Welcome emails
- Report generation (user-triggered)

**Normal**: Standard background tasks
- Scheduled maintenance
- Cache warming
- Analytics processing

**Low**: Non-urgent maintenance
- Log cleanup
- Old data archival
- Non-critical aggregations

### 3. Retry Strategy

**Use Exponential Backoff** for transient failures:
```php
// External API calls, network operations
JobPayload::create(
    job: $apiCallJob,
    retryStrategy: new ExponentialBackoffStrategy(
        maxAttempts: 5,
        baseDelaySeconds: 60,
        maxDelaySeconds: 3600
    )
);
```

**No Retries** for permanent failures:
```php
// Invalid input, business logic violations
JobPayload::create(
    job: $validationJob,
    retryStrategy: null  // Fail immediately
);
```

**Custom Schedule** for specific requirements:
```php
// Time-sensitive operations
JobPayload::create(
    job: $timeSensitiveJob,
    retryStrategy: new CustomRetryStrategy([30, 60, 120])  // Quick retries
);
```

### 4. Monitoring

**Track Key Metrics**:
- Queue depth by priority
- Average job execution time
- Success/failure rates
- Dead letter queue size
- Worker health status

**Set Up Alerts**:
```php
// Alert on high queue depth
if ($stats['total_size'] > 10000) {
    $alerting->send('Queue depth exceeded threshold');
}

// Alert on high failure rate
$failureRate = $stats['persistence']['failed'] / $stats['persistence']['total_jobs'];
if ($failureRate > 0.05) {  // 5% failure rate
    $alerting->send('High job failure rate detected');
}

// Alert on dead letter queue growth
if ($dlqStats['count'] > 100) {
    $alerting->send('Dead letter queue requires attention');
}
```

### 5. Resource Management

**Implement Timeouts**:
```php
JobPayload::create(
    job: $job,
    timeout: Duration::fromMinutes(5)  // Kill job after 5 minutes
);
```

**Manage Memory**:
```php
final readonly class LargeDataProcessingJob
{
    public function handle(): array
    {
        // Process in batches to avoid memory exhaustion
        foreach ($this->getBatches() as $batch) {
            $this->processBatch($batch);

            // Force garbage collection
            gc_collect_cycles();
        }
    }
}
```

**Connection Pooling**:
```php
// Reuse connections across jobs
final readonly class DatabaseJob
{
    public function handle(): array
    {
        // Use connection pool instead of new connections
        $connection = $this->connectionPool->acquire();

        try {
            // Execute queries
            $result = $connection->query($sql);
        } finally {
            // Always release back to pool
            $this->connectionPool->release($connection);
        }
    }
}
```

### 6. Error Handling

**Graceful Degradation**:
```php
final readonly class EmailJob
{
    public function handle(): array
    {
        try {
            $this->emailService->send($this->email);
            return ['status' => 'sent'];
        } catch (EmailProviderException $e) {
            // Log error but don't fail job
            $this->logger->warning('Email provider unavailable', [
                'error' => $e->getMessage()
            ]);

            // Use fallback provider
            $this->fallbackEmailService->send($this->email);
            return ['status' => 'sent_via_fallback'];
        }
    }
}
```

**Detailed Error Logging**:
```php
try {
    $job->handle();
} catch (\Throwable $e) {
    $this->logger->error('Job failed', [
        'job_id' => $jobId,
        'job_class' => get_class($job),
        'error' => $e->getMessage(),
        'trace' => $e->getTraceAsString(),
        'attempts' => $attempts,
        'metadata' => $payload->metadata->toArray()
    ]);

    throw $e;
}
```

### 7. Testing

**Unit Test Jobs**:
```php
it('sends welcome email', function () {
    $job = new SendWelcomeEmailJob(
        userId: UserId::generate(),
        email: new Email('test@example.com'),
        userName: new UserName('Test User')
    );

    $result = $job->handle();

    expect($result['sent_at'])->toBeGreaterThan(0);
});
```

**Integration Test Queue Operations**:
```php
it('processes jobs in priority order', function () {
    $queue = new RedisQueue($connection);

    // Push jobs with different priorities
    $queue->push(JobPayload::create($lowJob, QueuePriority::low()));
    $queue->push(JobPayload::create($highJob, QueuePriority::high()));
    $queue->push(JobPayload::create($normalJob, QueuePriority::normal()));

    // Pop and verify order
    $first = $queue->pop();
    expect($first->job)->toBe($highJob);

    $second = $queue->pop();
    expect($second->job)->toBe($normalJob);

    $third = $queue->pop();
    expect($third->job)->toBe($lowJob);
});
```

## Framework Integration

### With Event System

```php
// Dispatch jobs in response to events
#[OnEvent(priority: 100)]
public function onUserRegistered(UserRegisteredEvent $event): void
{
    $job = new SendWelcomeEmailJob(
        userId: $event->userId,
        email: $event->email,
        userName: $event->userName
    );

    $this->queue->push(JobPayload::immediate($job));
}
```

### With Scheduler

See [Scheduler-Queue Pipeline](scheduler-queue-pipeline.md) for complete integration documentation.

```php
// Schedule recurring job dispatch
$scheduler->schedule(
    'send-daily-digest',
    CronSchedule::fromExpression('0 8 * * *'),  // Every day at 8 AM
    fn() => $this->queue->push(JobPayload::immediate(new SendDailyDigestJob()))
);
```

### With Command Bus

```php
// Dispatch jobs via command handlers
final readonly class CreateUserHandler
{
    public function handle(CreateUserCommand $command): User
    {
        $user = $this->userRepository->create($command);

        // Queue welcome email
        $this->queue->push(JobPayload::immediate(
            new SendWelcomeEmailJob($user->id, $user->email, $user->name)
        ));

        return $user;
    }
}
```

## Console Commands

The framework provides console commands for queue management:

```bash
# Monitor queue status
php console.php queue:status

# Process jobs (worker)
php console.php queue:work --queue=default --timeout=60

# Retry failed jobs
php console.php queue:retry --queue=default --limit=50

# Clear queue
php console.php queue:clear --queue=default

# View failed jobs
php console.php queue:failed --limit=20

# Clean up old jobs
php console.php queue:cleanup --older-than=7d
```

## Performance Characteristics

**Redis Queue**:
- Throughput: 10,000+ jobs/second
- Latency: <5ms per operation
- Memory: ~200 bytes per job (serialized)
- Scalability: Horizontal (add Redis instances)

**File Queue**:
- Throughput: 1,000+ jobs/second
- Latency: <20ms per operation
- Memory: Minimal (disk-based)
- Scalability: Vertical (single server)

**Job Execution**:
- Overhead: <10ms per job (framework overhead)
- Timeout: Configurable per job
- Memory: Depends on job complexity
- Concurrency: Multiple workers supported