Sascha Trifunovic

How to scale a Laravel application with Memcache

How to scale a Laravel application with Memcache

Want to learn how to create a Laravel application that is ready to scale? In this tutorial we’ll explore how to create your Laravel application, hook it up to a Manifold project, and finally learn how to use Memcache to speed it up.

Memcache is a technology that improves the performance and scalability of web apps and mobile app backends. You should consider using Memcache when your pages are loading too slowly or your app is having scalability issues. Even for small sites, Memcache can make page loads snappy and help future-proof your app.

In this guide we will create a task list application based on the Laravel 5.2 tutorial and scale it with Memcache. The sample app in this guide can be found here.

Prerequisites

Before you complete the steps in this guide, make sure you have all of the following:

  • Familiarity with PHP (and ideally some Laravel).

  • A Manifold account. Sign up for free here.

  • The Manifold CLI installed and logged in.

  • PHP and Composer installed on your computer.

Create a Laravel application

To start, we create a Laravel skeleton app like so:

1$ composer create-project laravel/laravel --prefer-dist laravel_memcache
2Installing laravel/laravel (v5.7.15)
3 - Installing laravel/laravel (v5.7.15): Loading from cache
4Created project in laravel_memcache
5...
6
7$ cd laravel_memcache

If you want, you can run the app with php artisan serve and visit it at http://127.0.0.1:8000/. You will see the Laravel skeleton page.

Set up a Manifold project for our application

To manage the resources we will use, create a new Manifold project:

1$ manifold projects create laravel-memcache
2$ manifold init --project laravel-memcache

Now create a read-credentials API token to read the Manifold credentials.

1$ manifold tokens create

Add MANIFOLD_PROJECT=laravel-memcache and MANIFOLD_API_TOKEN=<YOUR_TOKEN> to your .env file.

To use the Manifold resources in Laravel, we need to install and publish Manifold’s service provider:

1$ composer require manifoldco/manifold-laravel
2$ php artisan vendor:publish

Now you can access all configuration variables stored within Manifold with config("RESOURCE_NAME.CONFIG_VARIABLE").

Add task list functionality

Let’s add a task list to the app that enables users to view, add, and delete tasks. To accomplish this, we need to:

  • Set up the database

  • Create a Task model

  • Create the view and controller logic

Set up the database

For this simple example we will use an SQLite database. In a production application you should use a more serious database than SQLite. You can easily get a hosted database on Manifold, such as the JawDB database service.

To use SQLite, make sure you have php-sqlite installed and configure the SQLite connection in config/database.php:

Embedded content: https://gist.github.com/jeffandersen/fa036174b9704283deb0147b8bf0fd1d#file-sqlite-database

To use this connection, set DB_CONNECTION=sqlite in your app's .env file.

Create the Task model

Now we have an empty database. To create and store tasks, we need to do three things:

Step 1) Create a migration that will create the tasks table:

1$ php artisan make:migration create_tasks_table --create=tasks

Tasks should have names, so let’s add name to the tasks table in the newly created database/migrations/_create_tasks_table.php file:

Embedded content: https://gist.github.com/jeffandersen/56ece97399cfd3ccf535950096823e1e#file-date-_create_tasks_table-php

Step 2) Create a Task model to easily access the tasks table from our code:

1$ php artisan make:model Task

This creates an empty Task model in app/Task.php. Laravel automatically infers its fields from the migration.

Step 3) Create the database and apply the migration:

1$ touch database/database.sqlite
2$ php artisan migrate --force

Add a view for the task list

To view the tasks stored in the database, we create a view that lists all tasks. We start with a boilerplate layout:

Embedded content: https://gist.github.com/jeffandersen/ae77edacfc02bfa6aa2f7f45110df099#file-app-blade-php

We can now create the task list view as a child view of the above layout:

Embedded content: https://gist.github.com/jeffandersen/70d22abea58d7aaaffbe0c484bb8d76c#file-tasks-blade-php

Ignore the TODOs for now (we'll fill them out later). To be able to access this view, connect it to the top route in routes/web.php:

Embedded content: https://gist.github.com/jeffandersen/92e0e5f75c34c5c62879c44d677176f1#file-router-php

If you have a local setup, you can now start a web server with php artisan serve and visit the view at localhost:8000. However, there isn't much to look at yet because the task list is empty.

Enable task creation

In order for the task list to be more useful, users need to be able to add tasks. Let’s create a card for that:

Embedded content: https://gist.github.com/jeffandersen/04f2566b5de1ba61b123ed5f30c79c95#file-tasks-blade-php

Because the task name is provided by the user, we need to make sure the input is valid. In this case, the name must exist, and it must not exceed 255 characters. If the input fails to validate according to these rules, we want to display the following error view:

Embedded content: https://gist.github.com/jeffandersen/61b7c76a3aee19e13ac9049c33345a21#file-errors-blade-php

Let’s add these new views to routes/web.php:

Embedded content: https://gist.github.com/jeffandersen/386372f43a2914ff522865a9e63f4611#file-tasks

Starting a local web server with php artisan serve and visiting localhost:8000 is now a bit more interesting because you can add tasks.

Enable task deletion

To complete our task list, we also need to be able to remove completed tasks. To delete a task, we add a Delete button to each item in the task list:

Embedded content: https://gist.github.com/jeffandersen/8d7ddae9ceeaf4ff7bccb8f785508545#file-tasks-blade-php

Then we wire this functionality to the appropriate route in routes/web.php:

Embedded content: https://gist.github.com/jeffandersen/a203afed48a5d8485e1631233d0bbecf#file-delete-task

Run php artisan serve and test the application by adding a few tasks. We now have a functioning task list. With this complete, we can learn how to improve its performance with Memcache.

Add caching to Laravel

Memcache is an in-memory, distributed cache. Its primary API consists of two operations: SET(key, value) and GET(key). Memcache is like a hashmap (or dictionary) that is spread across multiple servers, where operations are still performed in constant time.

The most common use for Memcache is to cache expensive database queries and HTML renders so that these expensive operations don’t need to happen over and over again.

Set up Memcache

To use Memcache in Laravel, you first need to provision an actual Memcache cache. You can easily get a free MemCachier cache from Manifold which provides you a fast and flexible Memcache compatible with the memcached protocol.

1$ manifold create --product memcachier-cache --plan dev --region us-east-1 memcachier

This creates a MemCachier cache with the name memcachier. There are three configuration variables now present in your Manifold config: MEMCACHIER_SERVERS, MEMCACHIER_USERNAME, and MEMCACHIER_PASSWORD. You can also find these on your cache's analytics dashboard which you can access via manifold sso memcachier.

Configure your Memcache

To use Memcache locally you will need to install some dependencies:

  1. Install the php-memcached PECL extention via your OS package manager.

  2. Uncomment ;extension=memcached.so in /etc/php/conf.d/memcached.ini.

  3. Run php -m to make sure the memcached module is loaded.

To set up Memcache in Laravel, we add the following dependency to composer.json:

1$ composer require ext-memcached

We then configure the cache in config/cache.php:

Embedded content: https://gist.github.com/jeffandersen/8329f166b2d9b8369fe06616c60be958#file-configure-memcached

This configures Laravel’s caching engine with MemCachier, which allows you to use your Memcache in a few different ways:

  • Directly access the cache via get, set, delete, and so on

  • Cache results of functions with the rememberForever function

  • Use Memcache for session storage

  • Cache rendered partials

  • Cache entire responses

Cache expensive database queries

Memcache is often used to cache the results of expensive database queries. Of course, our simple task list does not have any expensive queries, but let’s assume for this tutorial that fetching all of the tasks from the database is a slow operation.

The rememberForever function makes it easy to add caching to Laravel. You provide two arguments to it:

  • A cache key

  • A function that queries your database and returns results

The rememberForever function looks up the key in your cache. If the key is present, its corresponding value is returned. Otherwise, the database function you provided is called. Whatever that function returns is then stored in the cache with the corresponding key for future lookups.

This means that the first time you call rememberForever, the expensive database function is called, but every successive call to rememberForever obtains the value from the cache.

Use the rememberForever function to easily add caching to the task view controller in routes/web.php:

Embedded content: https://gist.github.com/jeffandersen/19ffc37fa49e29cf7ec05647c44ebb5e#file-show-tasks

As you might have noticed, we now have a problem if we add or remove a task. Because rememberForever fetches the task list from the cache, any changes to the database won't be reflected in the task list. For this reason, whenever we change the tasks in the database, we need to invalidate the cache:

Embedded content: https://gist.github.com/jeffandersen/9004135fa68cdf327859fbfc8d6d8dc8#file-add-and-delete-tasks

View Memcache statistics

To help demystify Memcache caching operations, we can visualize what’s going on under the hood.

First, we obtain stats every time the task list is requested in routes/web.php:

Embedded content: https://gist.github.com/jeffandersen/84f416639886e22168259758c68a670b#file-get-tasks-cached

Then, we add a card for the stats at the bottom of the task view:

Embedded content: https://gist.github.com/jeffandersen/31d7fc8551107c10aeb8e4d314086a4c#file-tasks-blade-php

Run php artisan serve and see the how the stats change when you play with the task list. You can see that the first time you access the page, the Get misses increase by one. This is because the first time rememberForever is called, the task list is not in the cache. The Set commands also increase because the task list is saved to the cache. If you refresh the page, the misses stay the same, but the Get hits increase because the task list is served from the cache.

When you add a new task or delete a task, your misses will increase again because the cache was invalidated.

You can also see the same stats and more on the analytics dashboard of your cache from within your MemCachier account.

Caching rendered partials

With the help of laravel-partialcache, you can cache rendered partials in Laravel. This is similar to fragment caching in Ruby on Rails or snippet caching in Flask. If you have complex partials in your application, it’s a good idea to cache them because rendering HTML can be a CPU-intensive task.

Do not cache partials that include forms with CSRF tokens.

Our example does not include any complex partials, but for the sake of this tutorial, let’s assume that rendering the task name in the task list takes a lot of CPU cycles and slows down our page.

First, we need to add the laravel-partialcache package to our app:

1$ composer require spatie/laravel-partialcache

Second, let’s factor out the task name into a partial:

Embedded content: https://gist.github.com/jeffandersen/f67d408b984d7c27d3a11f322989a6b4#file-name-blade-php

Now we can import and cache this partial in our task view:

Embedded content: https://gist.github.com/jeffandersen/f1cfd915d5815e0520a5d0da99f90ec2#file-tasks-blade-php

This caches each task name partial with the ID as its key. Note that in this example, we never have to invalidate a cached partial because the name of a task can never change. However, if you add the functionality to change the name of a task, you can easily invalidate the cached partial with PartialCache::forget('task.name', $task->id);.

Let’s see the effect of caching the task name partials in our application by running php artisan serve. You should now see an additional Get hit for each task in your list.

Using Memcache for session storage

Memcache works well for storing information for short-lived sessions that time out. However, because Memcache is a cache and therefore not persistent, long-lived sessions are better suited to permanent storage options, such as your database.

Changing the session store from a file (default) to memcached can be done easily by setting the SESSION_DRIVER config var in your .env file:

1SESSION_DRIVER=memcached

Caching entire responses

In Laravel, it’s also easy to cache the entire rendered HTML response by using laravel-responsecache. This is similar to view caching in Ruby on Rails. This package is easy to use and has good documentation in its README. However, we cannot use it in our example because our task list contains forms with CSRF tokens. To use this package with Memcache, you have to set the config var RESPONSE_CACHE_DRIVER to memcached.

Further reading & resources

Special thanks to Sascha Trifunovic and the rest of the MemCachier team for submitting this post for us to publish on their behalf as part of Manifold’s 12 Days of Services.

MemCachier
MemCachierReliable and powerful memcache-as-a-service.
Try Freearrow_right
Stratus Background
StratusUpdate

Sign up for the Stratus Update newsletter

With our monthly newsletter, we’ll keep you up to date with a curated selection of the latest cloud services, projects and best practices.
Click here to read the latest issue.