on Articles Mythbusters

WordPress Transients API caching benchmarks

5 comments
wordpress
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInShare on RedditShare on StumbleUpon

WordPress Transients API is very powerful tool which allows temporary storing data in wp-options table. It is very similar to WordPress Options, but has an expiration time, which enables us to use it as a caching engine.

The best thing about WordPress Transients is that they 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.

Using the API is very simple, there are only 3 methods that can be used:

Saving Transients

For saving transients we are using set_transients method, which accept three parameters:

Example (this example is storing data for 3 minutes):

set_transient('transientKey', 'transientValue', 60*3);

Getting Transients

For retrieving the data stored using Transients API, we are using the method get_transients which expects only Transient key as a parameter.

Example:

get_transient('transientKey');

Deleting Transients

For deleting Transients data we should use delete_transient method with Transient key as a parameter.

Example:

delete_transient('transientKey');

Using WordPress Transients to cache remotely fetched data

Imagine a scenario where we must fetch last 50 Tweets from Twitter and show them on WordPress page. Normally, you would cache this Tweets in a file, but why not use powerful Transients instead.

This code is responsible for fetching my last 50 tweets:

function transientsTest()
{
	// setting some parameters
	$username 	= 'codeforest';
	$maxTweets 	= 50;
	$url 		= 'http://api.twitter.com/1/statuses/user_timeline.json?screen_name=' . $username . '&count=' . $maxTweets;

	timer_start();
	
	// getting the tweets
        $tweets = curlRequest($url);
	$tweets = json_decode($tweets);

	echo '<ul>';
		if (count($tweets)) {
			foreach ($tweets as $tweet) {
				echo '<li>' . $tweet->text . '</li>';
			}	
		} else {
			echo '<li>No tweets</li>';
		}
	echo '</ul>';

	timer_stop(1); echo ' seconds';
}

function curlRequest($url) {
	$ch=curl_init();

	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
	curl_setopt($ch, CURLOPT_TIMEOUT, 15);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	$response = curl_exec($ch);

	return $response;
} 

As you can see, this code will go to Twitter every time we refresh the page on which we are showing this Tweets. I am using timer_start and timer_stop functions to measure the time that is needed.

After 500 times I got an average of 1.2 seconds for this operation. That is just not acceptable.

Now, lets make use of the Transients and see what happens:

function transientsTest()
{
	// setting some parameters
	$username 	= 'codeforest';
	$maxTweets 	= 50;
	$url 		= 'http://api.twitter.com/1/statuses/user_timeline.json?screen_name=' . $username . '&amp;amp;amp;count=' . $maxTweets;

	timer_start();

	$tweets = get_transient('cfTweets');

	if (false === $tweets) {
		// getting the tweets
		$tweets = curlRequest($url);
		// put it in transients 
		set_transient('cfTweets', $tweets, 60*3);
	}


	$tweets = json_decode($tweets);

	echo '<ul>';
		if (count($tweets)) {
			foreach ($tweets as $tweet) {
				echo '<li>' . $tweet->text . '</li>';
			}	
		} else {
			echo '<li>No tweets</li>';
		}
	echo '</ul>';

	timer_stop(1); echo ' seconds';
}

I am showing just the main function. My timer is staying at 0.003 seconds. Now, that is a big difference.

You would say that this is normal because it is very expensive to fetch data from another server. Let see some other examples.

Caching WordPress menu using Transients API

So we have a big menu with sub menus and everything. I used a simple wp_nav_menu function to render this menu. When not cached, it takes 1 second on average to load the menu.

Let’s put it in the cache:

function getThemesMenu()
{
	$menu = get_transient('cfMenu');

	if (false === $menu) {
                // parameter echo will return the menu instead of echoing it
		$menu = wp_nav_menu( array( 'theme_location' => 'primary', 'echo' => 0 ) );
		set_transient('cfMenu', $menu, 60*3);
	}

	return $menu;
}

Benchmark shows that now it takes 0.005 seconds to retrieve and render the menu. That is really a huge difference.

But, we must clear this cache after the menu is saved, so that the changes are reflected on the site immediately. It is quite simple, just put this code in theme’s functions.php file:

 
function updateMenu()
{
	delete_transient('cfMenu');
}

add_action( 'wp_update_nav_menu', 'updateMenu' );

And that’s it. Now, transient will be cleared every time you save your menu.

Caching complex query results using WordPress Transients API

We can have quite complex queries in our WordPress site. General example for storing them using Transients:

function getComplexData()
{
if (false === ($data = get_transient('cfData'))) {
	$query = "SELECT wposts.*, 
			wpostmeta.meta_value AS some_status, 
			wpostmeta2.meta_value AS some_rating
		FROM {$wpdb->posts} AS wposts, 
			{$wpdb->postmeta} AS wpostmeta, 
			{$wpdb->postmeta} AS wpostmeta2
		WHERE wposts.ID = wpostmeta.post_id
			AND wposts.ID = wpostmeta2.post_id
			AND wpostmeta.meta_key = 'status'
			AND wpostmeta2.meta_key = 'average_rate'
			AND wposts.post_type = 'event'
			AND wposts.post_status = 'publish'
		ORDER BY some_status ASC, 
			some_rating DESC
		LIMIT $offset, $per_page
		;";
	$data = $wpdb->get_results($query);
	// store it for 24 hours
	set_transient('cfData', $data, 60*60*24);
}

return $data;
}

This query took around 0.5 seconds without cache and 0.006 seconds with Tranisents cache.

It is important to clear the transient data every time data changes in the database!

Conclusion

Big performance gains can be achieved with WordPress Transients API. If you use it together with memcached engine, that the gain would be even more impresive.

Are you using Transients or plan to use it? Share your thoughts below in the comments section.

Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedInShare on RedditShare on StumbleUpon



  • http://lukapeharda.com Luka Peharda

    Nice article! I didn’t think that transients can speed up basic stuff like menu generation.

    • http://codeforest.net Zvonko

      Thanks for the comment.

      Basically, if you have a lot of complex queries, Transients API is the first step of optimization. You can later install memcached and put Transients data in it.

  • http://imdev.in Devin Walker

    Excellent post. Transients are EXACTLY what I need to cache API request. You got me kick started in the right direction. I’ve heard a lot about transients at WordCamps and they’re great from what I can tell so far. How long should we store transient data for? How long is too long?

  • Pingback: Caché de datos en Wordpress (I): Transients API

  • Radium Themes

    Great posts, I know this post is old but it is still gold. Thanks.