Laravel Queues and Jobs (with Failure Handling)
Introduction to Queues
Queues allow you to defer time-consuming tasks (like sending emails or processing files) to be executed in the background, improving your application's response time.
Creating Jobs
Generate a new job:
php artisan make:job ProcessPodcast
Basic job structure:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessPodcast implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $podcast;
public function __construct($podcast)
{
$this->podcast = $podcast;
}
public function handle()
{
// Process the podcast...
}
}
Dispatching Jobs
Synchronous dispatch (for testing):
ProcessPodcast::dispatch($podcast)->onConnection('sync');
Asynchronous dispatch:
// Basic dispatch ProcessPodcast::dispatch($podcast); // Delayed dispatch (5 minutes) ProcessPodcast::dispatch($podcast)->delay(now()->addMinutes(5)); // Specific queue ProcessPodcast::dispatch($podcast)->onQueue('processing');
Queue Configuration
.env configuration:
QUEUE_CONNECTION=database # or redis, sqs, beanstalkd, etc.
Configuring queue workers (in config/queue.php
):
'connections' => [ 'database' => [ 'driver' => 'database', 'table' => 'jobs', 'queue' => 'default', 'retry_after' => 90, ], ],
Running Queue Workers
Start a basic worker:
php artisan queue:work
Options:
# Process a specific queue php artisan queue:work --queue=high,default # Process only one job (good for debugging) php artisan queue:work --once # Process with maximum time/memory limits php artisan queue:work --max-time=3600 --memory=128 # Run in daemon mode (using Supervisor recommended) php artisan queue:work --daemon
Failure Handling
Failed Jobs Table
Create failed jobs table:
php artisan queue:failed-table php artisan migrate
Handling Job Failures
1. Maximum Attempts:
public $tries = 3; // Maximum number of attempts
2. Timeout:
public $timeout = 120; // Job timeout in seconds
3. Retry Until:
public function retryUntil() { return now()->addMinutes(10); }
4. Failed Method:
public function failed(Exception $exception) { // Send notification, log, etc. }
Managing Failed Jobs
List failed jobs:
php artisan queue:failed
Retry failed jobs:
# Retry all failed jobs php artisan queue:retry all # Retry specific failed jobs php artisan queue:retry 1 2 3
Delete failed jobs:
# Delete a specific failed job php artisan queue:forget 5 # Delete all failed jobs php artisan queue:flush
Queue Priorities
Dispatching to different queues:
// High priority job ProcessPodcast::dispatch($podcast)->onQueue('high'); // Low priority job ProcessPodcast::dispatch($podcast)->onQueue('low');
Processing with priorities:
php artisan queue:work --queue=high,low
Testing Jobs
Test if job was dispatched:
// In your test Bus::fake(); // Perform action that should dispatch job Bus::assertDispatched(ProcessPodcast::class); // Or assert it wasn't dispatched Bus::assertNotDispatched(ProcessPodcast::class);
Best Practices
Always use
SerializesModels
trait when working with Eloquent modelsKeep job payloads small (store data in DB and pass IDs if needed)
Use Supervisor to keep queue workers running
Set appropriate timeout and retry values
Monitor your failed jobs regularly
Consider using Horizon for Redis queues in production