Laravel Queues Deep — Job Processing and Queue Workers
In this tutorial, you will learn about Laravel Queues Deep. We cover key concepts, practical examples, and best practices to help you master this topic.
Laravel queues process time-consuming tasks asynchronously with support for Redis, database, SQS, and Beanstalkd drivers, job batching, Rate Limiting, and Horizon monitoring.
What You'll Learn
By the end of this tutorial, you'll create and dispatch jobs, configure queue drivers, implement job batching, handle failed jobs, and tune worker processes for performance.
Why Queues Matter
Queues improve user experience by moving slow operations like email sending, image processing, and API calls out of the request lifecycle.
Real-World Use
An e-commerce application queues order confirmation emails, generates invoice PDFs, syncs products to a search engine, and processes payment Webhooks, all through Redis queues.
Queues Path
flowchart LR
A[Laravel Framework] --> B[Queues]
B --> C[Redis Queue]
B --> D[Jobs]
B --> E[Horizon]
B --> F[Batch Jobs]
B --> G{You Are Here}
style G fill:#f90,color:#fff
Creating Jobs
Generate and implement a job class.
php artisan make:job ProcessOrder
<?php
namespace App\Jobs;
use App\Models\Order;
use App\Services\PaymentGateway;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessOrder implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $timeout = 120;
public int $tries = 3;
public int $backoff = 5;
public function __construct(
public Order $order,
) {}
public function handle(PaymentGateway $gateway): void {
try {
$gateway->charge($this->order);
$this->order->markAsPaid();
} catch (PaymentException $e) {
if ($this->attempts() >= $this->tries) {
$this->order->markAsFailed($e->getMessage());
}
throw $e;
}
}
public function failed(\Throwable $e): void {
Log::error("Order processing failed", [
"order_id" => $this->order->id,
"error" => $e->getMessage(),
]);
}
}
Dispatching Jobs
Dispatch jobs in various ways.
<?php
// Basic dispatch
ProcessOrder::dispatch($order);
// Delayed dispatch
ProcessOrder::dispatch($order)->delay(now()->addMinutes(30));
// Custom queue
ProcessOrder::dispatch($order)->onQueue("high");
// Custom connection
ProcessOrder::dispatch($order)->onConnection("redis");
// Dispatch chain
Bus::chain([
new ProcessOrder($order),
new GenerateInvoice($order),
new SendConfirmationEmail($order),
])->dispatch();
Job Batching
Group jobs into batches with completion callbacks.
<?php
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
$batch = Bus::batch([
new ProcessFile($file1),
new ProcessFile($file2),
new ProcessFile($file3),
])->then(function (Batch $batch) {
Log::info("All files processed");
})->catch(function (Batch $batch, Throwable $e) {
Log::error("File processing failed", ["error" => $e->getMessage()]);
})->finally(function (Batch $batch) {
Cache::forget("processing_files");
})->dispatch();
return redirect()->route("files.status", ["batchId" => $batch->id]);
Queue Worker Configuration
Configure queue workers for production.
php artisan queue:work redis --queue=high,default --tries=3 --timeout=60 --sleep=3 --max-time=3600
<?php
// config/queue.php
"redis" => [
"driver" => "redis",
"connection" => "default",
"queue" => env("REDIS_QUEUE", "default"),
"retry_after" => 90,
"block_for" => 5,
"after_commit" => true,
],
Common Mistakes
1. Serializing Large Objects
Jobs serialize constructor data. Large models with relations increase Redis memory. Use only the model ID.
2. Not Handling Max Attempts
Without tries, jobs retry forever. Set tries and implement failed() to handle ultimate failures.
3. Ignoring after_commit
Without after_commit, jobs may dispatch before a model exists in the database.
4. Overlapping Queue Workers
Multiple workers processing the same queue can overload resources. Use Horizon for worker management.
5. Not Setting Timeout
Jobs that hang without a timeout occupy workers. Always set a reasonable timeout.
Practice Questions
1. What is a queue job?
A class that encapsulates a task to be processed asynchronously by a worker.
2. How do you handle a failed job?
Implement the failed() method or configure failed_jobs table and queue:failed command.
3. What is job batching?
Grouping jobs that dispatch together with completion callbacks.
4. What does after_commit do?
Delays job dispatch until the database Transaction is committed.
5. Challenge: Create a job with retry logic and failure handling.
<?php
class SyncProduct implements ShouldQueue {
public int $tries = 5;
public int $backoff = [10, 30, 60, 120, 300];
public function handle(): void {
$response = Http::post(config("services.sync.url"), $this->product->toArray());
if ($response->failed()) {
throw new \Exception("Sync failed: " . $response->body());
}
}
public function failed(): void {
$this->product->markSyncFailed();
Notification::send(Admin::first(), new SyncFailed($this->product));
}
}
FAQ
Mini Project: Order Processing Pipeline
Build a complete order processing pipeline with queues.
<?php
class PlaceOrder {
public function __invoke(array $orderData): void {
DB::transaction(function () use ($orderData) {
$order = Order::create($orderData);
Bus::batch([
new ProcessPayment($order),
new ReserveInventory($order),
])->then(function () use ($order) {
SendOrderConfirmation::dispatch($order);
})->catch(function () use ($order) {
$order->markAsFailed();
})->dispatch();
});
}
}
What's Next
Laravel Horizon Laravel Broadcasting Laravel Notifications
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro