How to Run Multiple Job Chains in a Batch

Used when you want to run multiple job chains in parallel for faster processing. Without a batch, job chains will execute one after another completing one chain at a time. If you use multiple job chains in a batch it will execute and complete all the job chains same time.

Make sure to add Batchable trait on all the jobs even though those are used for job chaining. Because those jobs now inside a batch.

Syntax

    Bus::batch(
        [ 
            [], //Job Chain 1
            [], //Job Chain 2 
            []  //Job Chain 3
        ] //Batch
    )

Example

Imagine you have to generate reports for 5 outlets in the country. You can use GenerateReport job class to generate reports for those 5 outlets in a batch. But that job required to run JobTaskOne and JobTaskTwo jobs before executing the GenerateReports Job. So it required a job chain then a batch to complete the task. Run series of job chains in a batch to accomplish this.

Setup a fresh Laravel application. Then create required job classes using below artisan command.

php artisan make:job GenerateReport
php artisan make:job JobTaskOne
php artisan make:job JobTaskTwo

Open GenerateReport job class and add below code. All three classes should use Batchable trait.

namespace App\Jobs;

use Illuminate\Bus\Batchable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;

class GenerateReport implements ShouldQueue
{
    use Queueable, Batchable;

    /**
     * Create a new job instance.
     */
    public function __construct(public string $outletName) {}

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        sleep(1);
        Log::info('Completed Report for '. $this->outletName);
    }
}

Open JobTaskOne and add below code.

namespace App\Jobs;

use Illuminate\Bus\Batchable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;

class JobTaskOne implements ShouldQueue
{
    use Queueable, Batchable;

    /**
     * Create a new job instance.
     */
    public function __construct(public string $outlet) {}

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        sleep(1);
        Log::info("Job Task one of ". $this->outlet);
    }
}

Open JobTaskTwo and add below code.

namespace App\Jobs;

use Illuminate\Bus\Batchable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;

class JobTaskTwo implements ShouldQueue
{
    use Queueable, Batchable;

    /**
     * Create a new job instance.
     */
    public function __construct(public string $outlet)    {}

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        sleep(1);
        Log::info("Job Task Two of ". $this->outlet);
    }
}

Now we need to create job chain array and combine those job chains to form a single array used for batch processing. Check below image for more information.

Job Chains in a Batch

Modify web.php like below.

  1. Create array that contain 5 outlets.
  2. Iterate outlets array to create job chains array that has JobTaskOne, JobTaskTwo and GenerateReport job instances.
  3. Push all created job chains to a single array. Refer above image for array structure.
use App\Jobs\GenerateReport;
use App\Jobs\JobTaskOne;
use App\Jobs\JobTaskTwo;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    //Log request
    Log::info("Received a get request on root route");

    //List of outlets
    $outlets = ['outlet_1','outlet_2', 'outlet_3','outlet_4','outlet_5'];

    //Laravel jobs array
   
    $generateReportBatch = [];

    //Create instances of GenerateReport job class
    foreach($outlets as $outlet)
    {
        $jobChain = [new JobTaskOne($outlet), new JobTaskTwo($outlet),new GenerateReport($outlet)];
        array_push($generateReportBatch, $jobChain );

    }
    //Batch processing about created list of Laravel jobs
    Bus::batch($generateReportBatch)->dispatch();
     
});

Run your Laravel application and queue workers. Then check Laravel logs.

Conclusion

  1. First it execute first job of all job chains.
  2. Next it execute the 2nd job of all job chains.
  3. Just like that it does not complete one chain first and continue to next in a batch processing.
  4. All the chains will complete in nearly same time by executing the last job of all job chains.
[2025-01-03 08:01:03] local.INFO: Received a get request on root route  

//First job of the chain of all the job chain arrays
[2025-01-03 08:01:05] local.INFO: Job Task one of outlet_1  
[2025-01-03 08:01:05] local.INFO: Job Task one of outlet_2  
[2025-01-03 08:01:05] local.INFO: Job Task one of outlet_3  
[2025-01-03 08:01:05] local.INFO: Job Task one of outlet_4  
[2025-01-03 08:01:05] local.INFO: Job Task one of outlet_5 

//Next job of the chain of all the job chain arrays
[2025-01-03 08:01:05] local.INFO: Job Task Two of outlet_1  
[2025-01-03 08:01:05] local.INFO: Job Task Two of outlet_2  
[2025-01-03 08:01:05] local.INFO: Job Task Two of outlet_3  
[2025-01-03 08:01:05] local.INFO: Job Task Two of outlet_4  
[2025-01-03 08:01:05] local.INFO: Job Task Two of outlet_5  

//Running the last job of the chain of all job chain arrays.
[2025-01-03 08:01:05] local.INFO: Completed Report for outlet_1  
[2025-01-03 08:01:05] local.INFO: Completed Report for outlet_2  
[2025-01-03 08:01:05] local.INFO: Completed Report for outlet_3  
[2025-01-03 08:01:05] local.INFO: Completed Report for outlet_4  
[2025-01-03 08:01:05] local.INFO: Completed Report for outlet_5