Programster's Blog

Tutorials focusing on Linux, programming, and open-source

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.

Older versions of Laravel referred to the environment variable as 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

Last updated: 1st September 2021
First published: 16th June 2021