Getting Started With Laravel Queues and Background Jobs
Steps
First we need to create our class for a background job. In this case I am creating a background job for sending a welcome email to a user.
php artisan make:job EmailUserWelcomeEmail
You will now see the class within /app/Jobs
The class will implement the ShouldQueue
interface.
To prevent issues with loading, put parameters in constructor and pass parameters using ::dispatch
\App\Jobs\JobSendUserWelcomeEmail::dispatch($newUser->getId());
Configure Laravel To Use Database Driver
When I first ran this, I saw that the code was not running these jobs in the background. It turns out this was because my config/queue.php
file had the queue driver set to sync
. This essentially means there is no queue and the jobs run immdediately.
(More info).
The simplest solution to get an asynchronous queue working was to switch to the database
driver.
Before enabling this driver, we need to make sure the queue table is created as well as a table to
hold details of any failed jobs:
php artisan queue:table
php artisan queue:failed-table
This creates the migration script to create the "jobs" table like so:
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
... and the failed jobs table like so:
Schema::create('failed_jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
Then you need to run migrations to run this new migration:
php artisan migrate
Specify the Database Driver
Now you can go to /config/queue.php
and update the queue driver like so:
/*
|--------------------------------------------------------------------------
| Default Queue Connection Name
|--------------------------------------------------------------------------
|
| Laravel's queue API supports an assortment of back-ends via a single
| API, giving you convenient access to each back-end using the same
| syntax for every one. Here you may define a default connection.
|
*/
'default' => env('QUEUE_CONNECTION', 'database'),
Also, make sure that if QUEUE_CONNECTION
is set in your .env file, that you update it to state database
.
QUEUE_DRIVER
instead. Refer here.
Configure Supervisor
After performing all of the steps above, I saw that my request was returning immediately, but I wasn't getting any emails.
Looking in the jobs
table, I could see the jobs there with an attempts
count set to 0.
It appears that nothing was actually running the queue. To do this, the easiest thing to do is
configure Supervisor, by adding the following configuration to the Supervisor config file:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/my-site/artisan queue:work database --sleep=3 --tries=3
autostart=true
autorestart=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/my-site/storage/logs/worker.log
In this example, the numprocs directive will instruct Supervisor to run 1 queue:work processes and monitor it, automatically restarting it if it fails. Logs of errors will be redirected to the log file within laravel's storage directory.
References
- Stack Overflow - Passing parameters to Laravel job is not working
- Laravel Docs- Supervisor Configuration
- Laravel Docs - Dealing With Failed Jobs
- riptutorial.com - Queue Driver Configuration
- Laracasts - Queue It Up - Episode 1
- Laravel Docs - Generating Job Classes
First published: 16th June 2021