What happen if one job of a Laravel Batch processing failed? Imagine 3rd job of the GeneratReport job class failed.
- $this->batch()->cancelled() will return true from 3rd job to rest of the jobs.
- You can use this $this->batch()->cancelled() to cancel rest of the jobs in the batch or continue executing. Just use return statement in a if else statement using above cancelled() method to stop executing rest of the jobs.
Full example
Imagine you have to generate reports using GenerateReport job class for 6 outlets. One report failed during the processs. Log the failed report in Laravel Logs and Process GenerateReport jobs in a batch processing.
Setup a fresh Laravel web application. Create a Laravel job class using below artisan command.
php artisan make:job GenerateReport
Open GenerateReport Job class and add followings.
- Add Batchable trait.
- Accept outlet name and status (used to purposely failed a selected job)
- Add 2 seconds sleep to simulate time consuming task.
- Throw an exception from the failed the job if the status variable is true.
- Use cancelled() method on the batch() method to check whether any job of the batch has failed.
- Log information.
<?php
namespace App\Jobs;
use Exception;
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, public bool $status) {}
/**
* Execute the job.
*/
public function handle(): void
{
sleep(2);
if($this->status){
Log::info('Throw new exception - '.$this->outletName);
throw new Exception();
}
if ($this->batch()->cancelled()) {
// Determine if the batch has been cancelled...
Log::info('Job has been cancelled '. $this->outletName);
return;
}
Log::info('Completed Report for '. $this->outletName);
}
}
Open web.php
- Create list of outlets names in a array.
- Create a empty array to hold initialized instances of GenerateReport jobs.
- Create array of GenerateReports jobs by iterating outlets array.
- Randomly set status to false on one instance of the GenerateReport job in the array.
- Dispatch jobs array in a batch.
<?php
use App\Jobs\GenerateReport;
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','outlet_6' ];
//Laravel jobs array
$generateReportJobs = [];
//Create instances of GenerateReport job class
foreach($outlets as $key=>$outlet)
{
if($key == 2){
Log::info("Marked as a failed job $outlet");
array_push($generateReportJobs, new GenerateReport($outlet, true));
}else{
array_push($generateReportJobs, new GenerateReport($outlet, false));
}
}
//Batch processing about created list of Laravel jobs
Bus::batch($generateReportJobs)->dispatch();
});
Run this Laravel web application. Here I am using 3 queue workers to quickly finish the batch processing. For local development you can run below artisan command in 3 terminal windows.
php artisan queue:work
Logs:
- Thrown exception is captured by batch()->cancelled() method within the same job and remaining set of the jobs.
- You can either cancel or continue remaining set of jobs by handing the thrown exception separately.
[2025-01-01 11:56:04] local.INFO: Received a get request on root route
[2025-01-01 11:56:04] local.INFO: Marked as a failed job outlet_3
[2025-01-01 11:56:07] local.INFO: Completed Report for outlet_1
[2025-01-01 11:56:08] local.INFO: Completed Report for outlet_2
[2025-01-01 11:56:09] local.INFO: Throw new exception - outlet_3
[2025-01-01 11:56:09] local.ERROR: {"exception":"[object]"
[2025-01-01 11:56:10] local.INFO: Job has been cancelled outlet_4
[2025-01-01 11:56:11] local.INFO: Job has been cancelled outlet_5
[2025-01-01 11:56:12] local.INFO: Job has been cancelled outlet_6