Understanding Laravel Cache to Prevent High Disk IO

Arnold C Subastil
6 min readAug 14, 2019

--

Photo by Eugenio Mazzone on Unsplash

As a previous front-end developer, this is my first time working on a project that uses a framework (my previous job mostly focuses on layouts and website aesthetics). Given that it was a first-timer’s mistake, ignoring the usage of cache is quite a big one.

What exactly happens?

One part of my current role now includes monitoring of the app and checking if it follows the standard so that it works on its best potential. Monitoring is one of the most important things that a developer should do after the development phase. This observation and recording of different activities help us to gain additional information that can be used to improve and to check whether it is working properly or not. Once an application is deployed on the production server, developers are going to monitor the application’s activities.

Performance issue

One part of the application that caught our attention was the cache storage folder of our project that uses Laravel as the framework. As the team monitors the server performance of the application, we found out that the server has an issue when the number of users arises; a large number of files are saved in the cache folder(/storage/framework/cache/data).

Trust me, the number of folders can become thousands in no time.

After some research, analysis, and investigation we found out the cause of this problem, we’ve been ignoring some processes that saved some files on that folder, it occupies large storage in a high I/O scenario of the app. That is why we need to fully understand the usage of cache in the project, especially in using a framework such as Laravel.

Using Memcached

In the meantime, to solve the problem of having an enormous number of unused files — or maybe it is necessary, but I have no idea how important it is and why as of the moment — , we decided to use Memcached as the cached driver of Laravel. We replaced the default value CACHE_DRIVER from ‘file’ to ‘memcached’ on .env file to have an effect on config/cache.php, wherein the constant used in this code:

'default' => env('CACHE_DRIVER', 'file')

as memcached.org describes:

Memcached is a free and open source, high performance distributed memory object caching system, generic in nature, but intended for use in speeding dynamic web applications by alleviating database load. Memcached is simple yet powerful. Its simple design promotes quick deployment, ease of development, and solves many problems facing large data caches. Note that if you want to use memcached as your cache driver, you need to install Memcached PECL package on your server.

Sure, it resolves the problem by not creating a file and instead storing the cache on memcached driver. I think it should be over, right? Well, the solution did not delete the cache files that are currently saved on the folder, also these cache files are not deleted over time (it must be set the same as Cached::Forever). At this point, we just have to manually delete all the created cache files saved on the cache folder.

This problem opens an opportunity to learn about cache.
Also, it opens the question:

why does the application use cache automatically?

Using Laravel Cache

Laravel gives us these built-in functions to handle this kind of caches, and these are:

  • Cache::Put()
  • Cache::Get()
  • Cache::Forever()
  • Cache::Has()

To use any of these functions we need to use Illuminate/Contracts/Cache/Factory and Illuminate/Contracts/Cache/Repository, this gives us access to Laravel caching services.

The only problem is that we never used any of the functions stated above. Not in any part of our code in the project. Well, not that we are aware of.

In order to investigate we chose one of our projects in our local machine to monitor when it is saving a cache. To start, we set the CACHE_DRIVER to ‘file’ again so that the data may be visible for investigation.

During the testing of all the actions of the app, we notice that every time it saves a cache, it gives this data on storage/framework/cache/data folder:

cache file example of laravel

Knowing cache, the ‘1562547945’ is a key to access the value set on this file. In this case, the value is ‘1’. To further explain, if we intentionally saved a cache with the value ‘this is a cache!’, the supposed content of the cache file is 1562547945:this is a cache!

So the first question is, “where is this ‘1’ value came from?”

Using Laravel debugbar, we try to monitor which part of the app that saves a cache file. Along with the manual process of trial and error, one by one, we follow every code running during a process. The cache was created only when the API route is called. It seems that the cache is created when we visit our web routes, but that only happens because we also call our APIs via ajax. We notice that every time we access the API side of the app, it creates the cache files. We found out that the API Middleware of Laravel has a built-in throttling.

Seeing this as the only difference of API on other routes on app/Http/Kernel.php:

app/Http/Kernel.php

It uses a ‘throttle’, and then we notice this on the lower part of the Kernel:

It uses a ‘throttle’ and then, we notice this on the lower part of the Kernel

This ThrottleRequests file which leads to this /vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php, uses this package: `use Illuminate\Cache\RateLimiter;`

Which has this function:

function on throttle regarding cache

Where it uses cache to monitor the number of API calls and creates a limit of access every minute. This is the most simple way to explain throttle.

demo on how laravel uses the throttle

We’ve created a sample route on API route that is under the API middleware:

API route

As you can see, the number of times it was accessed was recorded on the cache file on the left, counting it until 60 (the limit for accessing it under a minute), then returns an error once it exceeded.

Using Laravel’s debugbar, we can immediately check if the page has been cached and provides sufficient information to be easily observed.

sample interface for debugbar

This is a lot easier than manually checking our codes and inserting some test codes, which may lead to us forget to remove those codes.

Now that we know that the throttling is the one that saves the cache files, we can turn it off if we think this is unnecessary for the project.

Conclusion

In conclusion, we learned the two ways why the framework is saving a cache, first one is the Cache facade, wherein we are going to use it intentionally, depending on our process of the application, and the other one is the Throttle, the one that Laravel is using to monitor the number of calling to API, the number ‘1’ saved on our example is the number of times the API route was access.

It is advisable to use Memcached as the CACHE_DRIVER to prevent this issue in storage, especially when we don’t need much of cache. Memcached is suitable for high I/O of the app.

This gives us two options:

  1. Turn off the throttling.
  2. Keep the throttling on and use memcached as the CACHE_DRIVER.

Both of these options prevent saving unnecessary cache files.

In case it happens again, or a similar problem arises, we suggest that we must always investigate the parts of the app that causes a large consumption of storage and the things that might affect the I/O once many users used it. Also, using debugging tools such as Laravel debugbar, minimizes the time of investigation of the flow of the app. It is a great help, especially for beginners that need to understand these kinds of topics, especially cache files.

References:
https://memcached.org/
https://laravel.com/docs/5.8/cache
https://aws.amazon.com/elasticache/redis-vs-memcached/
https://searchstorage.techtarget.com/definition/cache
https://vegibit.com/laravel-cache-tutorial/
https://github.com/barryvdh/laravel-debugbar

One more, we hire:

--

--