Skip to main content

Faster Eloquent Chunking in Laravel users best news for devlopers | Laravelnote

As your applications scale, processing large amounts of database records with Laravel Eloquent can become increasingly difficult. Resulting in out of memory exceptions and overall slowing down your application. Why is that?

When fetching results from the database you are in turn pulling that data into memory. Take this snippet of code for instance

Post::all()->each(function ($post) {
    // ...
});

Which results in the following query, loading all records in the posts table into memory

select * from posts;

Typically for tables with a small number of records, this is absolutely acceptable. However, as you accumulate tens of thousands of posts you will eventually begin hitting memory resource constraints of your webserver.

Chunking | Laravelnote

A common approach in Laravel is to use Eloquent's (via BuildsQuerychunk() method which fetches a fixed amount of records breaking a larger set into more consumable chunks.

Post::chunk(1000, function ($post) {
    // ...
});

While this might seem fine there are both improvements and gotchas to be aware of.

First, imagine the following scenario: you are fetching Post records from the database to update an attribute that you are also using in a where clause

Post::where('published_at', '<', now())->chunk(1000, function ($post) {
    $post->update('published_at', now());
});

Although contrived, this exemplifies a very real problem where such a query would result in an endless loop as the published_at attribute will always be less than now() at the time of the queries next execution (assuming accuracy to the second as with MySQL's timestamp column type or similar).

Second, there is the matter of the queries performance and its impact on the database server. The above code would result in a query similar to the following

select * from posts order by posts.id asc limit 1000 offset 9000

MySQL is unable to go directly to the offset due to deleted records and additional query constraints and, therefore, this query has to effectively select the first 10,000 records to return only the last 1,000 selected. As you can imagine this will not scale well into many-thousands or even millions of rows. This will cause your database server to use unnecessary resources slowing down all other queries from your application.

Chunking... but better!

In order to both prevent the unforeseen gotcha and improve database server performance we can use Eloquent's chunkById method

Post::where('published_at', '<', now())->chunkById(1000, function ($post) {
    $post->update('published_at', now());
});

The above code snippet will result in a query similar to the following

select * from posts where published_at < '2019-09-11 12:00:00' and id > 9000 order by id asc limit 1000

Why is this approach considered "better"?

a) it allows MySQL to completely skip the first 9000 (assumingly sequential) records
b) we will no longer be re-selecting records which we have already updated due to the id constraint in our where clause

Bonus - How?! 🤔

Diving into the chunkyById method of the BuildsQueries trait we see that the id of the last record fetched (remember, we're ordering by id in ascending order) is stored and used as a parameter in the next query to be run.

Popular posts from this blog

Laravel8 in Serializes Models trait | laravelnote

This article was originally posted, with additional formatting, on my personal blog at laravel serializes model Background  When dispatching an object onto the queue, behind the scenes Laravel is recursively serializing the object and all of its properties into a string representation that is then written to the queue. There it awaits a queue worker to retrieve it from the queue and unserialize it back into a PHP object (Phew!). Problem When complicated objects are serialized, their string representations can be atrociously long, taking up unnecessary resources both on the queue and application servers. Solution Because of this, Laravel offers a trait called SerializesModels which, when added to an object, finds any properties of type Model or Eloquent\Collection during serialization and replaces them with a plain-old-PHP-object (POPO) known as a ModelIdentifier. These identifier objects represent the original properties Model type and ID, or IDs in the case of an Eloquent\Collection,

Laravel Parallel Testing Is Now Available in laravel8 | Laravelnote

 Parallel Testing | Laravelnote As such we know Laravel and PHP Unit execute your tests sequentially within a single process.  As such laravel check the single process doesn’t use multiple cores so that therefore, your test execution is seriously bottlenecked! we glad to say that Parallel Testing is now available in Laravel. You can use this Laravel version8.25 you may also use to laravel8 built-in test Artisan command to run your cmd to tests simultaneously across multiple processes to use significantly reduce the time required for to run the entire test suite. It is about sure that in laravel8 new on top of Paratest Laravel automatically use to handles creating and migrating a test for database for each parallel process. In The  Laravel8 for testing purpose goodies - such as Storage::fake - are ready for used in Parallel too. Laravel Provide Each all individual laravel8 version use test suite will receive a varying benefits from parallel testing. In The Laravel Tests are execution wa

What is HTTP client in laravel8 by laravenote 2021 | Laravelnote

Laravel provides an expressive, minimal API around the Guzzle HTTP client, allowing you to quickly make outgoing HTTP requests to communicate with other web applications. Laravel's wrapper around Guzzle is focused on its most common use cases and a wonderful developer experience. Before getting started, you should ensure that you have installed the Guzzle package as a dependency of your application. By default, Laravel automatically includes this dependency. However, if you have previously removed the package, you may install it again via Composer: composer require guzzlehttp/guzzle Making Requests To make requests, you may use the get, post, put, patch, and delete methods provided by the Http facade. First, let's examine how to make a basic GET request to another URL: use Illuminate\Support\Facades\Http; $response = Http::get('http://example.com'); The get method returns an instance of Illuminate\Http\Client\Response, which provides a variety of methods that may be use