Queue Management and Background Processing in Production Laravel

John Finch

John Finch

prological

6 m read
Queue Management and Background Processing in Production Laravel

Laravel's queue system transforms time-consuming tasks into responsive background processes, ensuring applications remain fast and scalable. Laravel's queue system allows you to defer these long-running tasks to run in an ordered or controlled parallel manner, ensuring the server remains responsive and users do not have to wait. This guide covers comprehensive queue management strategies for production environments.

Queue Fundamentals and Configuration

Queue Driver Selection

Configure queue connections in config/queue.php for production reliability:

// .env configuration
QUEUE_CONNECTION=redis // Recommended for production
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

Redis provides superior performance and reliability compared to database queues. For high-volume applications, consider managed services like AWS ElastiCache or Redis Cloud.

Creating Background Jobs

Generate job classes using Artisan commands:

php artisan make:job ProcessPayment

// Generated job class
<?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 ProcessPayment implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public $timeout = 120;
public $tries = 3;

public function __construct(private $payment) {}

public function handle()
{
// Process payment logic
$this->payment->process();
}

public function failed($exception)
{
// Handle job failure
Log::error('Payment processing failed: ' . $exception->getMessage());
}
}

Production Queue Workers

Supervisor Configuration

For production, you need a way to keep your queue:work processes running. We need a program that supervises the worker process, restarts it if it fails, and potentially scales the number of processes.

Create supervisor configuration for reliable worker management:

# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600 --max-jobs=1000
autostart=true
autorestart=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/worker.log

--max-jobs tells Laravel that this worker can only process 1000 jobs. After it reaches the limit it'll be shut down. Then memory will be freed up and supervisor restarts the worker. --max-time tells Laravel that this worker can only live for an hour.

Supervisor Management Commands

# Update supervisor configuration
sudo supervisorctl reread
sudo supervisorctl update

# Start workers
sudo supervisorctl start laravel-worker:*

# Monitor worker status
sudo supervisorctl status

# Restart workers during deployment
sudo supervisorctl restart laravel-worker:*

Queue Priority and Multiple Queues

Queue Prioritization Strategy

Since payments are the most important jobs it's probably a good idea to separate them and handle them with priority. The same can be true for notifications as well.

// Job dispatching with priorities
ProcessPayment::dispatch($payment)->onQueue('payments');
SendNotification::dispatch($user)->onQueue('notifications');
GenerateReport::dispatch($data)->onQueue('default');

Worker Configuration for Priorities

# High-priority workers (payments first)
php artisan queue:work --queue=payments,notifications,default

# Notification workers
php artisan queue:work --queue=notifications,default

# General workers
php artisan queue:work --queue=default

Configure different supervisor groups for each priority:

[program:payment-worker]
command=php /var/www/html/artisan queue:work --queue=payments,notifications,default
numprocs=2

[program:notification-worker]
command=php /var/www/html/artisan queue:work --queue=notifications,default
numprocs=3

[program:default-worker]
command=php /var/www/html/artisan queue:work --queue=default
numprocs=2

Laravel Horizon Implementation

Horizon Installation and Configuration

For Redis-powered applications, Laravel Horizon provides elegant queue management:

composer require laravel/horizon
php artisan horizon:install
php artisan vendor:publish --provider="Laravel\Horizon\HorizonServiceProvider"

Configure production environment in config/horizon.php:

'environments' => [
'production' => [
'supervisor-1' => [
'connection' => 'redis',
'queue' => ['payments', 'notifications', 'default'],
'balance' => 'auto',
'maxProcesses' => 10,
'minProcesses' => 1,
'tries' => 3,
'timeout' => 60,
],
],
],

The auto strategy, which is the configuration file's default, adjusts the number of worker processes per queue based on the current workload of the queue. For example, if your notifications queue has 1,000 pending jobs while your render queue is empty, Horizon will allocate more workers to your notifications queue until the queue is empty.

Horizon Supervisor Configuration

# /etc/supervisor/conf.d/horizon.conf
[program:horizon]
process_name=%(program_name)s
command=php /var/www/html/artisan horizon
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/horizon.log
stopwaitsecs=3600

Memory Management and Performance

Worker Memory Optimization

Queued jobs can cause some memory leaks. Unfortunately, I don't know the exact reasons but not everything is detected by PHP's garbage collector. As time goes, and your worker processes more jobs it uses more and more memory.

Implement memory management strategies:

# Memory-conscious worker configuration
php artisan queue:work --max-jobs=1000 --max-time=3600 --memory=512

# Monitor memory usage
php artisan queue:monitor redis:default,redis:payments --max=100

Job Timeout Configuration

Configure appropriate timeouts in your job classes:

class ProcessLargeFile implements ShouldQueue
{
public $timeout = 300; // 5 minutes
public $tries = 2;
public $maxExceptions = 1;

public function retryAfter()
{
return 60; // Retry after 1 minute
}
}

Production Deployment Integration

cPanel Queue Setup

Based on your deployment notes, configure queues in cPanel environments:

# Set up queue worker for background processing
cd /home/myLaravelApp/public_html

# Process queued jobs
/opt/cpanel/ea-php82/root/usr/bin/php /home/myLaravelApp/public_html/artisan queue:work --max-time=60 --sleep=3 --tries=3 --max-jobs=50

# Add to cron for automatic restart
# */5 * * * * /opt/cpanel/ea-php82/root/usr/bin/php /home/myLaravelApp/public_html/artisan queue:restart

Deployment Automation

Create deployment scripts that handle queue workers:

#!/bin/bash
# deployment/deploy.sh

# Stop existing workers
php artisan queue:restart

# Clear and cache optimizations
php artisan config:cache
php artisan route:cache

# Restart supervisor workers
sudo supervisorctl restart laravel-worker:*

# Start Horizon (if using)
php artisan horizon:terminate
sleep 5
php artisan horizon

Job Batching and Advanced Features

Batch Processing

Handle large datasets efficiently with job batching:

use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;

$batch = Bus::batch([
new ProcessUser($user1),
new ProcessUser($user2),
new ProcessUser($user3),
])->then(function (Batch $batch) {
// All jobs completed successfully
})->catch(function (Batch $batch, Throwable $e) {
// Handle batch failure
})->finally(function (Batch $batch) {
// Cleanup operations
})->dispatch();

Unique Jobs

Prevent duplicate job processing:

class ProcessOrder implements ShouldQueue, ShouldBeUnique
{
public $uniqueFor = 3600; // 1 hour

public function uniqueId()
{
return $this->order->id;
}
}

Monitoring and Maintenance

Queue Health Monitoring

Implement comprehensive monitoring:

# Check queue status
php artisan queue:monitor redis:default --max=100

# View failed jobs
php artisan queue:failed

# Retry failed jobs
php artisan queue:retry all

# Clear failed jobs
php artisan queue:flush

Horizon Dashboard Access

Configure Horizon dashboard authentication:

// App\Providers\HorizonServiceProvider
public function gate()
{
Gate::define('viewHorizon', function ($user) {
return in_array($user->email, [
'admin@yourdomain.com',
]);
});
}

Performance Optimization

Redis Configuration

Optimize Redis for queue performance:

# redis.conf optimizations
maxmemory 2gb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
save 60 10000

Worker Scaling Strategy

The default production environment is configured to start a maximum of 10 worker processes and automatically balance the number of worker processes assigned to each queue.

Scale workers based on load:

// config/horizon.php
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 20,
'minProcesses' => 2,
'balanceMaxShift' => 3,
'balanceCooldown' => 3,
],
],
],

Effective queue management ensures Laravel applications handle background processing reliably and efficiently. Through proper supervisor configuration, strategic queue prioritization, and comprehensive monitoring, production environments can process thousands of jobs seamlessly while maintaining optimal performance and fault tolerance.

Featured Items

Scalable E-commerce Web Application Inspired by Industry Leaders
by Prological in PHP Scripts

$59.00

(1)

Comments (0)

Sign in to post a comment

No comments yet. Be the first to comment!