WordPress, Object Cache, and Redis
Tags: wordpress, redis, php, performance
Published: 2022-08-15 22:03:00

_Originally published at [my blog](https://felipeelia.dev/wordpress-object-cache-and-redis/)._

<!-- wp:paragraph -->
<p>Do you want to <strong>make your WordPress website faster while leveling up your WP programming skills</strong>? Object Cache may be your answer!</p>
<!-- /wp:paragraph -->

## Table of Contents
* [Before we go into Object Cache… What is cache?](#before-we-go-into-object-cache-what-is-cache)
* [WordPress Object Cache](#wordpress-object-cache)
* [How to “store” things in memory? Redis or Memcached](#how-to-store-things-in-memory-redis-or-memcached)
	* [What do I need to use WordPress Object Cache with Redis?](#what-do-i-need-to-use-wordpress-object-cache-with-redis)
* [The Redis Object Cache WordPress plugin](#the-redis-object-cache-wordpress-plugin)
	* [Same Redis for different sites at the same server](#same-redis-for-different-sites-at-the-same-server)
* [How the Object Cache plugin works](#how-the-object-cache-plugin-works)
* [Let’s give a look at some code](#lets-give-a-look-at-some-code)
* [Cache functions available in  WordPress](#cache-functions-available-in-wordpress)
	* [Key, value, group, and expiration](#key-value-group-and-expiration)
	* [Expiration](#expiration)
		* [Event-driven](#event-driven)
		* [Time-driven](#time-driven)
* [Transients API and Object Cache](#transients-api-and-object-cache)

<!-- wp:paragraph -->
<p>In the <a href="https://felipeelia.dev/what-is-an-api/">What is an API</a> post, I've briefly mentioned WordPress Object Cache and how I am using Redis here in the blog. In this post, we will give a better look into both of those things.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="antes-de-object-cache-o-que-e-cache">Before we go into <em>Object Cache</em>... What is cache?</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Before we start talking about Redis, we need to talk about Object Cache. And before we talk about Object Cache -- you saw it coming -- we need to talk about cache in general.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Looking for definitions of what is cache on the Internet, common sense seems to be something like this:</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><!-- wp:paragraph -->
<p>Cache is an <strong>intermediate storage with quick access</strong>, placed between the consumer and the main storage, potentially saving a longer trip.</p>
<!-- /wp:paragraph --></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p>Look at the example below. In the first request, we go all the way through the origin server but the server cache saves a copy of the response. In the second request, the copy is served, <strong>saving time by not going to the origin server</strong>.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center","id":2110,"width":768,"height":384,"sizeSlug":"large","linkDestination":"media"} -->
<figure class="wp-block-image aligncenter size-large is-resized"><a href="https://felipeelia.com.br/wp-content/uploads/2022/08/cache-definition-lg2x.webp"><img src="https://felipeelia.com.br/wp-content/uploads/2022/08/cache-definition-lg2x-1024x512.webp" alt="Two line diagram: in the first one the request goes from the client to the cache server and reaches the origin server. In the second one, the cache server answers the request." class="wp-image-2110" width="768" height="384"/></a><figcaption class="wp-element-caption">Image credits: <a href="https://www.keycdn.com/support/cache-definition-explanation">KeyCDN blog</a>.</figcaption></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>With that in mind, we can start thinking about all the several trips needed to visit a website and all the intermediate steps we can insert to save time:</p>
<!-- /wp:paragraph -->

<!-- wp:table {"className":"is-style-stripes"} -->
<figure class="wp-block-table is-style-stripes"><table><tbody><tr><td><strong>Browser cache</strong></td><td>Between your browser and the site server, if there is a <strong>valid copy of the page on your computer</strong>, we save a trip. In fact, we save the entire trip in this case.</td></tr><tr><td><strong>CDN</strong></td><td>If you are using a CDN like Cloudflare on your website and <strong>if there is a valid copy of the content in the CDN</strong>, there is no reason to ask for a new copy. It is enough to send just the copy and we save a trip.</td></tr><tr><td><strong>WP's cache</strong></td><td>In your WordPress website, if a post was already brought from the database and <strong>if the version of the post in memory is still valid</strong>, why go to the database again?</td></tr></tbody></table></figure>
<!-- /wp:table -->

<!-- wp:paragraph -->
<p>There are lots of other intermediate places we could save a version of the information, these are just some examples.  </p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="o-object-cache-do-wordpress">WordPress Object Cache</h2>
<!-- /wp:heading -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><!-- wp:paragraph -->
<p><em>WordPress Object Cache</em> is simple and understand how it works can make the difference during your next job interview ;-)</p>
<!-- /wp:paragraph --></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p>As I said in the <a href="https://felipeelia.dev/what-is-an-api/">What is an API</a> post, memory access is always faster than disk access. With that in mind, <strong>if we brought a post from the database and put that in memory, we do not need to go to the database to fetch it again</strong>.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center","id":2119,"sizeSlug":"full","linkDestination":"media"} -->
<figure class="wp-block-image aligncenter size-full"><a href="https://felipeelia.com.br/wp-content/uploads/2022/08/memory-hierarchy.png"><img src="https://felipeelia.com.br/wp-content/uploads/2022/08/memory-hierarchy.png" alt="Extended Memory Hierarchy. From bottom to top: Off-line Storage (slower), Hard Disk, RAM, Cache, CPU Registers (faster.) The faster, the more expensive it is." class="wp-image-2119"/></a><figcaption class="wp-element-caption">The image quality is not very good but the image is great to understand the relationship between storage and speed.</figcaption></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>The problem is that in WordPress default implementation, <strong>it will be in memory only until the end of the request</strong>. If the user refreshes the page, we will need to go to the database again.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>MySQL is smart enough to notice if a piece of information was requested recently and will make it available in a quicker way (basically storing it in its own memory part). <strong>Although faster, it will not be as faster as not going to MySQL at all.</strong> </p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>By default, this post (or any other piece of info we want) requested at the beginning of the execution is <strong>stored using a class called <a href="https://developer.wordpress.org/reference/classes/wp_object_cache">WP_Object_Cache</a></strong>. Its main goal is to save trips to the database and it works basically using key-value pairs. The problem is, like I said, all that only lasts one request. For the next one, we have to do everything again.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="como-gravar-coisas-na-memoria-redis-ou-memcached">How to "store" things in memory? Redis or Memcached</h2>
<!-- /wp:heading -->

<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column {"width":"66.66%"} -->
<div class="wp-block-column" style="flex-basis:66.66%"><!-- wp:paragraph -->
<p><strong>Softwares like <a href="https://redis.io/">Redis</a> or <a href="https://memcached.org/">Memcached</a> are in-memory databases.</strong> They can do more than that -- especially Redis -- but for our problem with WordPress, its key-pair value in-memory storage is everything we need.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In this post, I will use Redis as an example but Memcached installation and usage are very similar.</p>
<!-- /wp:paragraph --></div>
<!-- /wp:column -->

<!-- wp:column {"width":"33.33%"} -->
<div class="wp-block-column" style="flex-basis:33.33%"><!-- wp:block {"ref":2169} /--></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->

<!-- wp:heading {"level":3} -->
<h3 id="o-que-eu-preciso-para-usar-object-cache-com-redis-no-wordpress">What do I need to use WordPress Object Cache with Redis?</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The requirements to use WordPress Object Cache with Redis are quite simple:</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>A WordPress website</li><li>Redis running in your infrastructure</li><li>A WordPress plugin to connect WP to Redis</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>The best way to have step #2 done is to contact your hosting provider. On Cloudways, for example, you need to <a href="https://www.cloudways.com/blog/install-redis-cache-wordpress/#activating-redis-on-the-cloudways-platform">click on a button on their Dashboard to install Redis</a>.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center","id":1963,"width":512,"height":239,"sizeSlug":"full","linkDestination":"media"} -->
<figure class="wp-block-image aligncenter size-full is-resized"><a href="https://felipeelia.com.br/wp-content/uploads/2021/08/como-habilitar-redis-na-cloudways.jpg"><img src="https://felipeelia.com.br/wp-content/uploads/2021/08/como-habilitar-redis-na-cloudways.jpg" alt="How to install Redis on Cloudways: Server Management -&gt; Settings &amp; Packages -&gt; Packages -&gt; Redis (Install)" class="wp-image-1963" width="512" height="239"/></a></figure>
<!-- /wp:image -->

<!-- wp:heading -->
<h2 id="o-plugin-redis-object-cache">The <em>Redis Object Cache</em> WordPress plugin</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>As I said in the other post, the plugin I use is <a href="https://wordpress.org/plugins/redis-cache/">Redis Object Cache</a>. Install and configure it is not hard. Out of the box, it will try to connect to a Redis instance in the same server (<code>127.0.0.1</code>) on port <code>6379</code> using Redis database number <code>0</code>.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center","id":2048,"width":480,"height":216,"sizeSlug":"full","linkDestination":"custom"} -->
<figure class="wp-block-image aligncenter size-full is-resized"><a href="https://br.wordpress.org/plugins/redis-cache/"><img src="https://felipeelia.com.br/wp-content/uploads/2022/06/redis-object-cache-plugin.png" alt="Screenshot of the &quot;Redis Object Cache&quot; plugin in WordPress.org Directory." class="wp-image-2048" width="480" height="216"/></a></figure>
<!-- /wp:image -->

<!-- wp:paragraph -->
<p>To have the plugin working, it will need to copy its <code>object-cache.php</code> file from its own <em>includes</em> folder to the <em>wp-content</em> folder of your WordPress installation. If for whatever reason like directory permissions, it is not possible to copy the file automatically, you will need to copy it manually.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 id="mesmo-redis-com-diferentes-sites-no-mesmo-servidor">Same Redis for different sites at the same server</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>I have another blog running on this same server, sharing the same Redis installation. To avoid conflicts between stored information for each site, I had to define  <code>define( 'WP_REDIS_DATABASE', 0 );</code> on one and <code>define( 'WP_REDIS_DATABASE', 1 );</code> on the other.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To adjust the configuration you will need to place some constants in your <em>wp-config.php</em> file. The full list of settings is available on the <a href="https://github.com/rhubarbgroup/redis-cache/wiki/Connection-Parameters">plugin wiki</a>.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="como-o-plugin-de-object-cache-funciona">How the <em>Object Cache </em>plugin works</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Inside the <a href="https://developer.wordpress.org/reference/functions/wp_start_object_cache/">wp_start_object_cache()</a> function (see below), executed at the beginning of WordPress load flow, WP will detect if there is an <code>object-cache.php</code> drop-in or not. If it is available, the file is loaded, otherwise, the default implementation is used. After that, the cache is initiated.</p>
<!-- /wp:paragraph -->

<!-- wp:code {"className":"php"} -->
<pre class="wp-block-code php"><code>function wp_start_object_cache() {
	...
		if ( file_exists( WP_CONTENT_DIR . '/object-cache.php' ) ) {
			<strong>require_once WP_CONTENT_DIR . '/object-cache.php';</strong>
			...
		}
	...
 

	<strong>// If there is no external cache, loads WP's.</strong>
	if ( ! wp_using_ext_object_cache() ) {
		require_once ABSPATH . WPINC . '/cache.php';
 	}
 
	require_once ABSPATH . WPINC . '/cache-compat.php';
 
	...
	<strong>wp_cache_init();</strong>
	...
}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p><em>Redis Object Cache</em>'s <code>object-cache.php</code> contents can be seen <a href="https://plugins.trac.wordpress.org/browser/redis-cache/trunk/includes/object-cache.php">here</a>. It is formed by an implementation of the <code>WP_Object_Cache</code> class (last part of the file) and the several functions that use it like <code>wp_cache_add</code>, <code>wp_cache_get</code>, and  <code>wp_cache_delete</code>.</p>
<!-- /wp:paragraph -->

<!-- wp:image {"align":"center","id":2102,"width":360,"height":270,"sizeSlug":"full","linkDestination":"media"} -->
<figure class="wp-block-image aligncenter size-full is-resized"><a href="https://felipeelia.com.br/wp-content/uploads/2022/08/doc-back-to-the-future-ready.webp"><img src="https://felipeelia.com.br/wp-content/uploads/2022/08/doc-back-to-the-future-ready.webp" alt="Animated GIF of Doc Brown from &quot;Back to the Future&quot; holding electric cables and saying &quot;Ready!&quot;" class="wp-image-2102" width="360" height="270"/></a></figure>
<!-- /wp:image -->

<!-- wp:heading -->
<h2 id="dando-uma-olhada-em-codigo">Let's give a look at some code</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>If you need some code to understand things better that is okay, I am one of those too. Here is a part of the code executed when you call <code>get_post( 123 );</code> in WordPress 6.0.1.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The code starts in <a href="https://github.com/WordPress/WordPress/blob/6.0.1/wp-includes/post.php#L990">wp-includes/post.php</a>, where get_post calls <code>WP_Post::get_instance( $post )</code>.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Then, in <a href="https://github.com/WordPress/WordPress/blob/6.0.1/wp-includes/class-wp-post.php#L231">wp-includes/class-wp-post.php</a>:</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>final class WP_Post {
	...
	public static function get_instance( $post_id ) {
		<strong>$_post = wp_cache_get( $post_id, 'posts' );</strong>

		if ( ! $_post ) {
			...
			<strong>wp_cache_add( $_post-&gt;ID, $_post, 'posts' );</strong>
		} 
		...
	}
}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>At first, it checks if the cached value exists (<code>wp_cache_get</code>). If it exists it is used, otherwise, the post is fetched and then cached (<code>wp_cache_add</code>).</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>If we look at the <code>wp_cache_add</code> function code, we can see it is nothing more than a simple wrapper for the <code>add</code> method of the <code>WP_Object_Cache</code> instance stored in the global variable <code>$wp_object_cache</code>.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>function wp_cache_add( $key, $data, $group = '', $expire = 0 ) {
    global $wp_object_cache;
 
    return $wp_object_cache-&gt;add( $key, $data, $group, (int) $expire );
}</code></pre>
<!-- /wp:code -->

<!-- wp:heading -->
<h2 id="as-funcoes-de-cache-disponiveis-no-wordpress">Cache functions available in  WordPress</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>The <a href="https://developer.wordpress.org/reference/classes/wp_object_cache/#wp_cache-functions">official documentation lists some of the most important cache functions</a> and I am transcribing them here. WordPress's core itself uses them in several places but you can call them too in your theme or plugin.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>wp_cache_add( $key, $data, $group = '', $expire = 0 ) // If exists, do not overwrite
wp_cache_set( $key, $data, $group = '', $expire = 0 ) // If exists, overwrite
wp_cache_replace( $key, $data, $group, $expire ) // If does not exist, do nothing
wp_cache_get( $key, $group = '', $force = false, $found = null )
wp_cache_delete( $key, $group = '' )
wp_cache_flush()</code></pre>
<!-- /wp:code -->

<!-- wp:heading {"level":3} -->
<h3 id="chave-valor-grupo-e-expiracao">Key, value, group, and expiration</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Most of the <code>wp_cache_*</code> functions use four parameters:</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li><strong>Key:</strong> the identified for the object stored in cache. The post ID, for example.</li><li><strong>Value:</strong> the object stored in cache. In our example, the post.</li><li><strong>Group (optional):</strong> You can optionally group values. Previously, it was used simply to organize things but coming in WordPress 6.1, there will be a <a href="https://core.trac.wordpress.org/ticket/4476">new <code>wp_cache_flush_group</code> function</a> to flush all objects of a certain group. You will be able, for example, to delete just keys of a certain plugin.</li><li><strong>Expiration (optional):</strong> for how long a cached value should be considered valid. Let's give it a closer look now.</li></ul>
<!-- /wp:list -->

<!-- wp:heading {"level":3} -->
<h3 id="expiracao">Expiration</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>When we talk about cache, one of the most important concepts is expiration. <strong>Is the information we have still valid?</strong> This is a key part of any cache implementation, including WordPress Object Cache.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>So, how do we decide for how long a cached object should be considered valid?</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4 id="baseado-em-um-evento">Event-driven</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Let's say you are caching the list of posts of the biggest content. You get all the posts, apply the <code>the_content</code> filter in all of them, and check how many characters they have. Pretty intensive, right? This deserves to be cached.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>What can change the result of this process? Only if a post is created, deleted, or edited, right?</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In this case, our cache does not need to expire. <strong>It is enough to invalidate the result we have using a related <a href="https://felipeelia.com.br/o-que-sao-hooks/">hook</a></strong>.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4 id="baseado-em-tempo">Time-driven</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>If what you are caching comes, for example, from something external like an API, <strong>the event that invalidates the cache is not under our control</strong>. In this case, we need to <strong>check the results from time to time</strong> in the external API and update the value we have stored.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="transients-api-e-object-cache">Transients API and Object Cache</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>If you read the post or watched the YouTube video about the <a href="https://felipeelia.com.br/cache-facil-no-wordpress-transients-api/">WordPress Transients API</a>, the central idea here is not new: <strong>a key-value pair that avoids a long process is the essential idea behind both things</strong>. In fact, in a certain aspect, it is indeed the same thing. Look at the implementation of the <code>get_transient</code> function:</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>function get_transient( $transient ) {
    if ( wp_using_ext_object_cache() || wp_installing() ) {
        $value = <strong>wp_cache_get( $transient, 'transient' )</strong>;
    } else {
		...
    }
	...
}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Basically, if you are using an external mechanism for Object Caching, the transient will be stored there. Otherwise, the regular database will be used. Interesting, huh? Make sure to check that content too!</p>
<!-- /wp:paragraph -->