0

Queue connection config — queues table, horizon, workers

Intermediate5 min read·lv-19-003
sql

Concept

Failed jobs occur when a job exhausts all retry attempts. Laravel stores failed jobs in the failed_jobs table for inspection and manual re-queuing.

Setup: php artisan queue:failed-table && php artisan migrate creates failed_jobs. Or configure a DynamoDB / Null driver for failed jobs.

What's stored: The serialized job payload, the exception message and stack trace, the connection and queue name, and the failure timestamp.

queue:failed command: php artisan queue:failed — list all failed jobs. php artisan queue:retry {id} — retry a specific failed job. php artisan queue:retry all — retry all failed jobs. php artisan queue:forget {id} — delete a failed job. php artisan queue:flush — delete all failed jobs.

Customizing failed job storage: In config/queue.php, 'failed' key. Default: database-uuids driver using the failed_jobs table. Can use dynamodb, null (disable), or sqs (via custom driver).

Alerting on failures: The job.failed event is fired when a job fails. Listen to it in EventServiceProvider: \Illuminate\Queue\Events\JobFailed::class.

Queue::failing() / Queue::looping(): Callbacks on queue worker lifecycle events.

Horizon failed jobs: Laravel Horizon (lv-19-007) provides a web UI for browsing and retrying failed jobs.

Code Example

php
<?php
// Create failed_jobs table
// php artisan queue:failed-table
// php artisan migrate

// config/queue.php — failed job configuration
'failed' => [
    'driver'   => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
    'database' => env('DB_CONNECTION', 'mysql'),
    'table'    => 'failed_jobs',
],

// failed_jobs table structure:
// - id (UUID)
// - uuid
// - connection (string) — which queue driver
// - queue (string) — which queue
// - payload (longText) — serialized job
// - exception (longText) — exception with stack trace
// - failed_at (timestamp)

// Artisan commands for managing failed jobs
// php artisan queue:failed                     — list all
// php artisan queue:retry abc-uuid-123         — retry by ID
// php artisan queue:retry all                  — retry all
// php artisan queue:forget abc-uuid-123        — delete one
// php artisan queue:flush                      — delete all
// php artisan queue:prune-failed --hours=48    — delete older than 48h

// Listening for job failures — in EventServiceProvider
protected $listen = [
    \Illuminate\Queue\Events\JobFailed::class => [
        \App\Listeners\NotifyTeamOfFailedJob::class,
    ],
];

class NotifyTeamOfFailedJob
{
    public function handle(\Illuminate\Queue\Events\JobFailed $event): void
    {
        \Illuminate\Support\Facades\Log::critical('Queue job failed', [
            'connection' => $event->connectionName,
            'queue'      => $event->job->getQueue(),
            'payload'    => $event->job->getRawBody(),
            'exception'  => $event->exception->getMessage(),
        ]);

        // Could also send Slack notification, page on-call, etc.
    }
}

// Individual job failure handler
class ProcessOrder implements \Illuminate\Contracts\Queue\ShouldQueue
{
    use \Illuminate\Queue\InteractsWithQueue, \Illuminate\Foundation\Bus\Dispatchable;
    use \Illuminate\Bus\Queueable, \Illuminate\Queue\SerializesModels;

    public function failed(\Throwable $exception): void
    {
        // Called AFTER all retries exhausted and job is sent to failed_jobs
        \Illuminate\Support\Facades\Log::error('Order processing failed permanently', [
            'order_id'  => $this->orderId,
            'exception' => $exception->getMessage(),
        ]);
        // Notify customer, restore inventory, etc.
    }
}