Search code examples
phplaravelasynchronousjobs

Laravel Jobs are not asynchronous


I need a way to run some tasks asynchronously as the execution time varies between each task and I want to run the in an asynchronous way using Laravel Jobs and database as the driver.

I created to test jobs using the command line: php artisan make:job TestOne php artisan make:job TestTwo

TestOne.php

<?php

namespace App\Jobs;

use App\Jobs\Job;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class TestOne extends Job implements ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        sleep(5);
        foreach (range(1, 10) as $item)
            \Log::info("TestOne: item #" . $item);
    }
}

TestTwo.php

<?php

namespace App\Jobs;

use App\Jobs\Job;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class TestTwo extends Job implements ShouldQueue
{
    use InteractsWithQueue, SerializesModels;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        foreach (range(1, 10) as $item)
            \Log::info("TestTwo: item #" . $item);
    }
}

I simply log some messages in laravel's log file, and since TestOne is sleeping for 5 seconds, TestTwo should log the messages first

HomeController.php

<?php

namespace App\Http\Controllers;

use Queue;
use App\Jobs\TestOne;
use App\Jobs\TestTwo;

class HomeController extends Controller
{
    public function index()
    {
        $this->dispatch(new TestOne());
        $this->dispatch(new TestTwo());
        die("stop");
    }
}

However TestTwo job still waits until TestOne job is done:

[2017-03-04 17:00:30] local.INFO: TestOne: item #1  
[2017-03-04 17:00:30] local.INFO: TestOne: item #2  
[2017-03-04 17:00:30] local.INFO: TestOne: item #3  
[2017-03-04 17:00:30] local.INFO: TestOne: item #4  
[2017-03-04 17:00:30] local.INFO: TestOne: item #5  
[2017-03-04 17:00:30] local.INFO: TestOne: item #6  
[2017-03-04 17:00:30] local.INFO: TestOne: item #7  
[2017-03-04 17:00:30] local.INFO: TestOne: item #8  
[2017-03-04 17:00:30] local.INFO: TestOne: item #9  
[2017-03-04 17:00:30] local.INFO: TestOne: item #10  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #1  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #2  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #3  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #4  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #5  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #6  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #7  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #8  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #9  
[2017-03-04 17:00:30] local.INFO: TestTwo: item #10 

I am running the jobs with php artisan queue:listen

What am I doing wrong here? I really need these tasks to run asynchronously, just like, say, a JS AJAX request would work like.

I am using Laravel 5.2. Again, I am using "database" as the queue driver and yes I have migrated the jobs table. Is it not possible using the database as the driver?


Solution

  • To process jobs in parallel you'd have to split them across different queues as @dparoli pointed out.

    This way you can not only categorize them but also priorize how they'll be processed by your queue workers.

    When dispatching a job, you'll specify which queue it belongs:

    $this->dispatch((new TestOne())->onQueue('queue1'));
    $this->dispatch((new TestTwo())->onQueue('queue2'));
    

    That way you can spawn multiple queue workers to process jobs separately:

    php artisan queue:work --queue=queue1
    php artisan queue:work --queue=queue2
    

    You can also use a single queue worker which priorize how queues are processed, so you can give a higher or lower precedence for some jobs over others:

    php artisan queue:work --queue=queue2,queue1
    

    By using a process monitor like Supervisor you can even spawn a single worker in multiple processes as detailed in the documentation.

    It's worth noting that a single queue worker which prioritizes its queues will still process their jobs by taking the FIFO precedence in addition to the given queue priority. To achieve a better parallelism you'll want to spawn multiple queue workers.

    This holds true for all queue drivers.