Speeding Up Laravel Tests with tmpfs (MySQL in RAM)
If your Laravel test suite is painfully slow and does a lot of database work (multi-tenancy, migrations, seeding), the bottleneck is almost…
Seeding large datasets in Laravel can be really slow. Here's how to optimize your seeders for better performance.
Consider this typical seeder approach:
public function run()
{
foreach ($teams as $team) {
Project::factory()->count(100)->create([
'team_id' => $team->id
]);
}
}
This seems simple, but it's incredibly inefficient because it:
Here are key strategies to dramatically improve seeder performance:
Instead of creating records one by one, batch them:
// Bad: Individual insertions
Project::factory()->count(100)->create();
// Good: Bulk insertion
$projects = Project::factory()->count(100)->make()->toArray();
Project::insert($projects);
When dealing with thousands of records, chunk them to manage memory:
collect($projects)->chunk(100)->each(function ($chunk) {
Project::insert($chunk->toArray());
});
Collect all data before touching the database:
// Prepare all data first
$projects = [];
$projects = array_merge($projects, Project::factory()->count(100)->make()->toArray());
$projects = array_merge($projects, Project::factory()->count(50)->make()->toArray());
// Then insert in one go
collect($projects)->chunk(100)->each(function ($chunk) {
Project::insert($chunk->toArray());
});
When using bulk insertions, you need to manually format special fields:
$projects = collect($projects)->map(function ($project) {
return array_merge($project, [
'created_at' => now()->format('Y-m-d H:i:s'),
'settings' => isset($project['settings']) ? json_encode($project['settings']) : null,
]);
});
Here's a complete example that implements all these optimizations:
public function run(): void
{
$teams->each(function ($team) {
$now = now()->format('Y-m-d H:i:s');
$projects = [];
// Prepare data in memory
$projects = array_merge($projects,
Project::factory()->count(300)
->for($team)
->make()
->map(function ($item) use ($now) {
return [
'team_id' => $item->team_id,
'name' => $item->name,
'description' => $item->description,
'settings' => isset($item->settings) ? json_encode($item->settings) : null,
'created_at' => $now,
'updated_at' => $now
];
})
->toArray()
);
// Bulk insert in chunks
collect($projects)->chunk(100)->each(function ($chunk) {
Project::insert($chunk->toArray());
});
});
}
These optimizations can make your seeders run 10-100x faster, but there are some trade-offs:
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com
Thank you for this complete explanation, including realistic code examples and mentioning trade-offs. This allows developers to fully grasp the topic at hand and make informed decisions on whether to use this approach.