Scale up data imports with queue-based execution
The time has come to put it all together. We’ve slowly built on in the previous articles and have increased our speed. With batch inserting we were able to insert a batch of 100 rows (even though you can pick any number). With chunk reading, we eliminated the memory problem of loading the entire excel sheet into memory and instead loaded a 100 rows worth of data (even though again we could have increased or decreased that number to whatever we wanted).
But there’s still one more issue. What if the file is so large that it times PHP out? How do you import a file that takes 5 minutes to import? Even if PHP didn’t time out, would you want to make your user wait while the import was running? Of course not. That’s where queued imports shine.
How to Import with Queue?
First thing to note is that queued imports only work with chunk reading. That means that the WithChunkReading
concern must be present. After chunk reading is implemented into the import, the only other concern left to implement is ShouldQueue
. Could it be that simple? Yes.
<?php
namespace App\Imports;
use App\Models\User;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Hash;
use Maatwebsite\Excel\Concerns\ToModel;
use Maatwebsite\Excel\Concerns\WithChunkReading;
class QueuedImport implements ToModel, WithChunkReading, ShouldQueue
{
/**
* @param array $row
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new User([
'name' => $row['first_name'] . " " . $row['last_name'],
'email' => $row['email'],
'password' => Hash::make($row['password']),
]);
}
public function chunkSize(): int
{
return 20;
}
}
Let’s create our UserController
method and add our route and test. I’ll spoil it slightly, it won’t work right away unless you have something running.
<?php
namespace App\Http\Controllers;
//...
use App\Imports\QueuedImport;
use App\Models\User;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
class UserController extends Controller
{
//...
public function import_with_queue() {
Excel::import(
new QueuedImport,
'mock_data/MOCK_DATA_6.csv'
);
return redirect('/')->with('success', 'Users Imported Successfully!');
}
}
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Artisan;
//...
Route::get(
'/import-with-queue',
[UserController::class, 'import_with_queue']
);
Let’s call the route and see what happens. Well, it worked. The import was a success but it didn’t get sent to the queue and do it in the background. It just imported like everything else so far. You can see that the user was locked on the screen until the import finished.
Why didn’t the queue import work?
If it didn’t work, it’s because of the way that you have Laravel configured. Open your .env
file and you’ll notice that the QUEUE_CONNECTION
is set to sync
. We need to prepare Laravel to be able to use queues. Let’s modify it so that we can use queues. Run the following commands:
php artisan queue:table
This generates the jobs
migration where jobs will be inserted.
php artisan migrate
Creates the jobs
table. All that’s left is to modify the QUEUE_CONNECTION
to database
and clear the config. Open your .env
file and change the QUEUE_CONNECTION
from sync
to database
.
QUEUE_CONNECTION=database
Finally clear the config cache.
php artisan config:clear
Let’s try our example again. Do you think it’s going to work?
Well, it was lightning fast this time. It looked like the job was created but nothing happened. Where’s the import? One last step. We need to make sure that our queue is working.
php artisan queue:work
Cool! We even get an awesome chunk message display when each chunk is complete and how long it took the queue to import it.
2022-10-08 18:10:58 Maatwebsite\Excel\Jobs\QueueImport ....................... 39.51ms DONE
2022-10-08 18:10:58 Maatwebsite\Excel\Jobs\ReadChunk ....................... 989.82ms DONE
2022-10-08 18:10:59 Maatwebsite\Excel\Jobs\ReadChunk ....................... 916.27ms DONE
2022-10-08 18:11:00 Maatwebsite\Excel\Jobs\ReadChunk ....................... 920.93ms DONE
2022-10-08 18:11:01 Maatwebsite\Excel\Jobs\ReadChunk ....................... 920.62ms DONE
2022-10-08 18:11:02 Maatwebsite\Excel\Jobs\ReadChunk ....................... 914.32ms DONE
2022-10-08 18:11:03 Maatwebsite\Excel\Jobs\AfterImportJob ....................... 4.14ms DONE
Explicitly Stating Queue Import
When calling Excel::import
with the ShouldQueue
concern implemented, the queue is implicitly called. If you want to be explicit about it, you could use Excel::queueImport()
. Either way, the ShouldQueue
concern is always required.
Modifying the Queued Import
If you try modifying the details inside of the queued import class, you’ll notice that the changes are not reflected. Each time the import is modified, you will need to restart the queue.
Laravel Series
Continue your Laravel Learning.
Handle massive datasets with efficient chunk reading.
Laravel-Excel — P7: Chunk Reading
In the seventh installment of our Laravel-Excel series, discover how to handle large datasets by reading them in manageable chunks. Learn techniques to optimize memory usage, boost performance, and maintain data integrity for scalable, high-performing applications.
Scale up data imports with queue-based execution.
Laravel-Excel — P8: Queued Import
In the eighth installment of our Laravel-Excel series, discover how to utilize queued imports to handle large and complex data loads without blocking your application. Learn how to combine job management, chunk reading, and parallel processing for fast, reliable data ingestion.
Effortlessly generate Excel files for reliable data sharing.
In the eighth installment of our Laravel-Excel series, discover how to utilize queued imports to handle large and complex data loads without blocking your application. Learn how to combine job management, chunk reading, and parallel processing for fast, reliable data ingestion.