Caching in WordPress: Transients or APC?

When using WordPress, I often find myself needed to query out posts or other information that isn’t easily available courtesy of one of WordPress’ built-in functions. While it’s a normal thing to do, it can come with a cost: performance.

Every time a query is written to pull out specific data from WordPress, it adds another query to MySQL which adds time and performance. Singularly, it’s not a huge cost, but if your site has any decent level of traffic, it can add up.

Screen Shot 2013-12-09 at 10.56.01 AMCase in point: we recently relaunched our internal campus news site as a responsive site.

Every day, we post news stories for various audiences and some of these stories are for events happening today, which is information we wanted to feature for our readers.

We have a custom field in our WordPress backend that allows us to note the day of an event, so even if the story is posted a month ago, on the day of the event we can query it and display it for the user.

That query is pretty simple. It looks like this:


$args = array(
 'numberposts' => -1,
 'offset' => 0,
 'orderby' => 'post_date',
 'order' => 'DESC',
 'post_type' => 'post',
 'post_status' => 'publish',
 'year' => $current_year,
 'meta_key' => 'date',
 'meta_value' => $today,
 );

$go = get_posts($args);

With over 3,500 posts in this site, that’s an expensive query. You’ll notice we help narrow that query down a bit by only having WordPress look for posts in the current calendar year. No sense in looking at 2011’s posts for an event in 2013. Even with that reduction, there’s still several hundred posts to sort through.

There are a few ways to reduce the load that query creates. The best way is to cache the results of the query, which will allow us to serve that faster from memory/database then having to make a whole database call with all sorts of parameters.

One way is to use WordPress’ built-in Transients API. It’s a fairly straight-forward system to set, fetch and delete data inside WordPress. This is perfect for things like queries and other rendered data that you’re going to need often.

Setting up and fetching transients is easy.


$transientname = 'hewt';
$cachetime = 60;

if(false === ($go = get_transient($transientname))){
	$go = get_posts($args);
	set_transient($transName,$go,60*$cachetime);
}

That code, in a nutshell, is first checking to see if the transient variable exists and can be fetched. If it can, great, move on. If it can’t or doesn’t exist, run the WordPress query and save the results in the Transient API for an hour. We can start using the $go variable as needed after that.

The trick about WordPress transients is they are stored in the database. But Mike, didn’t you say that having to query the database every time is a performance hit? Yes, but…

Transients are inherently sped up by caching plugins, where normal Options are not. A memcached plugin, for example, would make WordPress store transient values in fast memory instead of in the database. For this reason, transients should be used to store any data that is expected to expire, or which can expire at any time. Transients should also never be assumed to be in the database, since they may not be stored there at all.

The other way to do some as-needed caching is to use a PHP caching solution, such as APC. I’ve talked about caching WordPress queries using APC in this post.

You’d write your PHP code to save to APC the same way.


$transientname = 'hewt';

if($go = apc_fetch($transientname)){}else{
    $go = get_posts($args);
    apc_add($transientname,$go,3600);
}

Same idea here. We’re checking APC to see if the variable is available. If it is, great, move on. If it’s not, run the WordPress query and save the results for the next time the page is loaded.

What’s neat about APC is that instead of writing the values to MySQL, APC stores them in memory, which means its much faster to read data than having to query the disk.

The challenge with APC is that it isn’t installed by default in most LAMP setups. You have to compile it in, and set some initial values. If you’re running WordPress on a shared server, chances are APC isn’t available.

Where I’ve run into challenges with the Transient API is when I need to delete the cache. There isn’t a button to just clear it out. You can do it programmatically using built-in functions, such as

delete_transient( $transientname );

The other catch, WordPress doesn’t delete old cached transients unless you ask for them by name. WPEngine wrote this about Transients API:

But with the WordPress Transients, you get different but still very undesirable behavior. Because the values are written to the database, not a fixed-sized block of RAM, they all stick around. Which means even with the heavily-loaded site, you still have your session data. Awesome!

Or so you thought. Because what isn’t said in the Transient API is what happens when you use unique keys like sessions. And what happens in with the built-in method is that the options table fills up indefinitely! Because: WordPress’s “old data clean up” only operates when you request the key (as we covered earlier). If you just leave the key, it’s left in the options table, forever. There’s no separate process that cleans these up

Stu Miller had this method for cleaning out old transients:

function purge_transients($older_than = '7 days', $safemode = true) {

Clearing out APC can be done in a few ways. You can delete a particular key:

apc_delete($transientname);

APC comes with a web app where you can view data about the cache, status of keys and more. You can log in to the backend and delete a variable there.

You can also do it with PHP outside WordPress by using something like this:

apc_clear_cache();
apc_clear_cache('user');
apc_clear_cache('opcode');

In review, WordPress transients are easy to use, built into every WordPress installation and don’t require additional plugins. Be aware though, that deleting expired and old values can be tricky and require additonal code. APC is fast, PHP-based and easy to access, but may not be available on all installations or server environments.

1 comment

Leave a Reply