Caching in with the Transients API

Written by | June 1, 2014 | Posted in Coding, Tutorials

Before we can really dig into the Transients API, we need to understand what “transients” are. In this case, a transient is a piece of information that you need to access periodically, which is stored temporarily in a cache.

With the Transients API, you are able to store a discrete set of data in a key-value pair that will expire after a pre-determined amount of time. The key would be the unique identifier for the data that you want to store.

The value can be a regular variable, or a whole object. The API will even handle serialization of complex data for you, so you can store some really serious stuff in there.

That sounds a lot like the Options API on the surface, but this has the added bonus of an expiration time. We’ll talk more later about how this works in detail, but for now just understand that the expiration time is the minimum amount of time that the data will stay in the cache.

Why should I use it?

We’re talking about server load optimization and response time optimization.

Because you can store any kind of data in a transient, you can cache responses from scripts that cause heavy server load or that take a long time. Set the expiration time for when you’d like the value to be refreshed, and you’ve dramatically increased the performance of a page on your site!

What can I use it for?

Long-running tasks / slow SQL queries

If you have a really complex SQL query with tons of joins that takes a while to run, you can cache the result of the query.

Fragment caching

I think a whole article could be devoted to fragment caching, so I don’t want to get too far down that rabbit hole. But basically fragment caching is what you do when you want to cache a whole block of code. It might contain a few queries inside it. But you’re not just caching the response of the queries. You’re caching the whole block of code, HTML and all. This is a great use case for the Transients API.

Responses from requests of external APIs

This is my current favorite use case for the Transients API. If you need to display data on your site from an external API that has rate limiting, the Transients API is your best friend. You can fetch the data once, set it to a transient, and then have the transient expire after an hour or so. Then you’re only hitting the external API for data once per hour instead of every single time the page loads.

What should I NOT use it for?

Single-use data storage

If you only need it once, why even cache it? If you set a transient, the only time it knows it’s expired and deletes it is if you check it again. If you store a bunch of transients that are never checked again, it’s going to fill up your cache.

Anything you can’t recreate on command

Since transients can actually disappear at any time, you can’t rely on getting a transient without having a fallback to get the data from the original source.

Something you need to be in the database, guaranteed

True, the transients might be stored in the database, but they might also be stored in an object cache. So when setting a transient, it’s important to also use the Transient API to also retrieve the transient (not the Options API or an SQL query).

How the Transients API works

There are just 3 functions:

set_transient( $transient, $value, $expiration );

Save transients using this function. You give it a name, the data you want to store, and an expiration time in seconds.

get_transient( $transient );

Fetch transients using this function. WordPress will check to see if the transient is still fresh. If it is, it returns the value. If it’s expired, it deletes the transient.

delete_transient( $transient );

Delete transients using this function. You may want to make sure that if a post is updated, the associated transients get deleted. You’d use this for that.

How transients are stored

Transients are pretty similar to options (being key-value pairs), except they have the added super-power of an expiration time. So it makes sense that they can be stored in the options table.

But the real power of the Transients API is that it can use object caching. So the Transients API is really leveraged by caching plugins and caching software like Memcached. To make the Transients API use object caching, you’ll need to install an object cache backend plugin to your WP site. There are plenty of options out there. I’ve got some links at the end of the slides to resources for this kind of thing.

The advantage of using object caching with the Transients API is that object caching uses RAM and is 10x-100x faster than retrieving data from the database.

How expiration works

When you set a Transient, you set an expiration time in seconds. That expiration time is more like a maximum age than an expiration date. So when you get the Transient (like on page load), it checks if that time has lapsed or not.

Transients aren’t tied to cron at all and they won’t expire on their own. It depends on the get_transient script running to say “yep, that’s expired.”

Like if some food in your fridge has gone bad, but you don’t know for sure until you open the container and check. There isn’t a little imp living in your fridge that gets rid of old food for you and your food won’t throw itself away. You have to check it and throw it out yourself.

So let’s say you have a certain transient expiration set for 30 minutes. You load the page at 3:00pm and the transient is not set. The page will set the transient for 30 minutes. Then the page is reloaded at 3:04pm. The transient is set (because it has only been 4 minutes). But say the page isn’t loaded again until 4:52pm. Because the transient is older than 30 minutes, the transient will be deleted. You can include in your script the fallback to re-create the data and re-set the transient. And then the new expiration time is 5:22pm.

Real-world example

Let’s imagine we have a website that displays a bunch of videos that are hosted on Vimeo. We want to get the poster image of the video from the Vimeo API and then play the video once someone clicks on the image.

We’ll add a custom field to the post that takes the URL of the video on Vimeo. We’ll have some code that uses that URL to fetch the data about that video from the Vimeo API.

// Get ID for Vimeo video added to post
$vimeo_url = get_field('vimeo_url');
$vid = substr($vimeo_url, strrpos($vimeo_url, '/') + 1);

// Get the info about the video from Vimeo
$info = $vimeo->request("/videos/$vid");

Not that it matters for the purpose of the demo, but we’ll also have the following HTML to show the video poster image and link to play the video:

<div id="video-wrapper">
  <a href="javascript:void(0)" class="play" rel="<?php echo $vimeo_url; ?>">
    <img src="<?php echo $info['body']['pictures'][2]['link']; ?>" />
  </a>
</div>

If we run the code above it will load the data directly from Vimeo and it will work just fine in development. But in a production environment, we’d probably max out the rate limiting and really bog down the speed of the site. So we want to store the $info in a transient and set it to expire in 1 hour.

// First, check to see if the transient is set (and fresh)
$info = get_transient('vimeo_cache_'.get_the_id());
// If the transient is not set or is expired, then do the following
if ($info === false) {
  // Get the info about the video from Vimeo
  $info = $vimeo->request("/videos/$vid");
  // Set the transient
  set_transient('vimeo_cache_'.get_the_id(), $info, 60 * 60); // 1 hour
}

To really leverage the Transients API, you should enable an object cache on your site. No changes are needed to your code because the Transients API will automatically use it if it is enabled.

Speed Comparisons

I ran some tests the other day and recorded the numbers of several random page loads using just the straight Vimeo API, the Transients API, and the Transients API with object caching enabled. You can see that the default implementation of the Transients API was over 300x faster than just loading the data from Vimeo each time.

With object caching enabled, it was over 3x faster than the default implementation! And 1300x faster than getting it from Vimeo each time. Now imagine this speed improvement for 20 or so videos on page.

Without Transients API Transients API (default) Transients API (object caching)
Time (s) 0.39472 0.00091 0.00025
Time (s) 0.33202 0.00099 0.00026
Time (s) 0.24812 0.00111 0.00022
Average (s) 0.32495 0.00100 0.00024
Improvement >300x faster >1300x faster

 

Resources and more information

Transients API:

Object caching: