You could consider a similar implementation to the one described in the docs:
https://laravel.com/docs/9.x/queues#dispatching-batches
Where you dispatch several jobs with the line bounds you want each process.
For example:
new ImportCsvLines($filename, 1, 100);
Would process a CSV file named $filename from lines 1 to 100.
It seems you are using The PHP League CSV package. If so, you can get the line count of a file using PHP's count() function. See how to do it on its docs:
https://csv.thephpleague.com/9.0/reader/#records-count
I tested with this code:
<?php
use App\Jobs\ImportCsvLines;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\LazyCollection;
use League\Csv\Reader;
Artisan::command('test', function () {
// $filePath = $file->store('reconciliation');
$filePath = 'reconciliation.csv'; // manually created a file with 6735 lines
$reader = Reader::createFromPath(storage_path('app/' . $filePath), 'r');
$reader->setHeaderOffset(0);
$count = count($reader);
$batches = LazyCollection::make(function () use ($count) {
for ($offset = 0; $offset < $count; $offset += 100) {
yield [$offset, min($count, $offset + 99)];
}
})->map(function ($chunk) use ($filePath) {
return new ImportCsvLines($filePath, $chunk[0], $chunk[1]);
});
Bus::chain($batches->all())->dispatch();
$this->info(memory_get_peak_usage(true) / 1024 / 1024);
});
And got a memory usage of 22 MB.
For reference, my test job is:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use League\Csv\Reader;
use League\Csv\Statement;
class ImportCsvLines implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public function __construct(
public string $filePath,
public int $from,
public int $to,
) {}
public function handle()
{
$reader = Reader::createFromPath(storage_path('app/' . $this->filePath), 'r');
$reader->addStreamFilter('convert.iconv.ISO-8859-15/UTF-8');
$reader->setDelimiter(',');
$reader->setHeaderOffset(0);
$records = Statement::create()
->offset($this->from)
->limit(100)
->process($reader);
foreach ($records as $offset => $record) {
logger()->info($offset, $record);
}
}
}