Pengantar API Transien

by

in
Everyone wants a fast website. The faster the site, the longer a user stays and clicks on pages or buys products. One of ways to speed up your site is to implement caching. The idea behind caching is to store information so that it’s accessed faster than connecting to the database would be. To use an analogy, it’s much faster to go to your fridge to get a snack than it would be to drive to the store, find it on the shelf, pay for it, and then drive home to eat it.

There are a number of types of caching; object caching, browser caching, page caching and even database caching. Each type has its purpose and setup steps that can range from installing a plugin to changing your server configuration to enable object caching. In this article, you will learn about the Transients API, which can be either object or database caching – depending on where it’s being used.

The Transients API is used to temporarily store information in WordPress across page loads. In a default installation, this information is stored in the options table of the database. This is very similar to the Options API but with the ability to set an expiration time after which the data will be deleted.

Transients are a very powerful tool that can be used to not only avoid needless database requests for page content but also speed up generating complicated markup and mitigate slow third-party API requests. 

Saving a transient

To save a transient, you can use the set_transient function. It accepts three parameters: the name of the transient, the data to be stored, and an optional expiration time in seconds. And it will return true if the transient was set.

The snippet below sets a transient with an expiration of one week.

set_transient( 'dev-blog-transient', 'Some data I want to persist', WEEK_IN_SECONDS );

Retrieving a transient

To retrieve the data stored in a transient, you can use get_transient. This function accepts a single parameter—the name of the transient to retrieve—and returns the data or false if the transient does not exist or has expired.

$persisted_data = get_transient( 'dev-blog-transient' );

It’s always best to check for the data using the === operator to confirm that  both data exists and are of the same type. There may be cases where the transient data is “falsey,” such as the number zero or the string “false,” but makes sense for what the data is being used for. Using strict comparison protects you against a false negative in that case.

The snippet below demonstrates a common approach to check for the transient data and sets you up to be able to regenerate the transient.

if ( false === ( $persisted_data = get_transient( 'dev-blog-transient' ) ) ) {
  // This will run if there is no transient or it has expired.
} 

Removing a transient

Any transient can be deleted at any time using the delete_transient function. This function also accepts a single parameter of the transient name and will return true if the transient was deleted.

delete_transient( 'dev-blog-transient' );

Object caching

Before you can fully understand the Transients API, you need to know how WordPress’ in-memory or object caching system works. Object caching allows you to store items that might be computationally expensive for later use in memory.

A great use-case for this is when you have an expensive WP_Query whose results are needed more than once. Caching the results allows you to avoid multiple trips to the database to retrieve the data and speeds up your processing time.

WordPress uses the WP_Object_Class behind the scenes to power this cache, but you would use the wp_cache_* family of functions to interact with it.

The default object cache is not persistent, meaning that anything that has been cached will disappear once the page is finished loading. It is possible to change the object cache to be persistent using a number of caching plugins. Setting this up is beyond the scope of this article but it usually requires modifications on the server and adding the object-cache.php drop-in to override the default object cache.

By this point, you’re probably wondering what this has to do with the Transients API. Well, once there is a persistent cache in-place, the Transients API will no longer store its data in the database via the Options API and will instead use WP_Object_Class to persist its data. The immediate benefit here is speed. Accessing data stored in memory can be much faster than querying the database for that same data.

The best part is that you don’t have to make any changes to your code to gain the performance benefits. The changes are all handled under the hood.

Transient expiration

The concept of transient expiration time is commonly misunderstood. Ryan McCue has an excellent quote in the official documentation that explains it nicely:

Everyone seems to misunderstand how transient expiration works, so the long and short of it is: transient expiration times are a maximum time. There is no minimum age. Transients might disappear one second after you set them, or 24 hours, but they will never be around after the expiration time.
Ryan McCue

This is the case due to a number of external factors such as database updates or the object cached being purged. As such, you should always assume that the transient doesn’t exist and write your code accordingly.

This also means that even if you set your transient to have an expiration time of 100 years from now, there is no guarantee that the data will persist until then.

Always set an expiration time

When setting a transient, you can leave out the expiration time parameter and have it default to 0 seconds.

set_transient( 'dev-blog-transient', 'Some data I want to persist' ); 

This effectively tells the API that this data should not be available after 0 seconds. While this may seem like a good approach for data that’s not needed for very long, it has a potentially nasty side effect that only pertains to when transients are being saved to the database.

As you now know, under the hood, transients are saved to the database via the Options API. But if you take a closer look at the source code, you will see that any transients without an expiration time will have the corresponding option set to autoload.

$autoload = 'yes';
if ( $expiration ) {
    $autoload = 'no';
    add_option( $transient_timeout, time() + $expiration, '', 'no' );
}
$result = add_option( $transient_option, $value, '', $autoload );

This means that this data will be automatically loaded for every page request, even if it’s never used. Imagine 1000 transients with large datasets being retrieved on every page load—this has a potentially huge impact on your site performance. 

The easiest way to avoid this issue (besides having an object cache in place) is to always set an expiration time, even if it’s only for one second.

set_transient( 'dev-blog-transient', 'Persisted but not autoloaded data', 1 ); 
If you’re not using an object cache and want to persist data during the page load but don’t need a transient, use wp_cache_set() and wp_cache_get() to set and retrieve data that will disappear after the page is finished loading.

When should you use transients?

This will depend on where your code is being run and if you know the environment.

In some cases you will know where your code is going to be run. Whether it’s a client site or your own. The server configuration is known to you and it’s possible to determine if there is a persistent object cache like Redis or Memcached in place. In this case, you can use the Transients API or the wp_cache_* functions as transients are being stored in the object cache as explained above.

If you’re releasing that plugin or theme publicly, there is no way to know what kind of server environment your code will be running in. In this case, it’s better to use the Transients API as it is guaranteed to save to the database.

TL;DR: If you need to ensure that data will be persisted in any server environment, then use the Transient API.

Code examples

Retrieving or regenerating transient data

Probably the most common example is when you want to check for a transient and generate it if it doesn’t exist.

In this example, you are retrieving a list of post IDs. In the case that the transient returns false, you generate the query and then use set_transient to store only the array of IDs returned from the query. The important part here is that the same variable name, $persisted_data,  is used for both retrieving the transient and the final generated list of IDs. This ensures the rest of your code will work regardless of whether the transient exists or not.

if ( false === ( $persisted_data = get_transient( 'dev-blog-post-id-list' ) ) ) {
    // The transient doesn't exist or has expired so you need to regenerate
    $query = new WP_Query( array( 'posts_per_page' => 25, 'fields' => 'ids' ) );
    if ( $query->have_posts() ) {
        $persisted_data = $query->posts;
    } else {
        $persisted_data = array();
    }
    set_transient( 'dev-blog-post-id-list', $persisted_data, WEEK_IN_SECONDS );
}

Priming the cache

A really great technique that can help with ensuring that your site visitors have a performant experience is to “prime” your caches. This essentially means that when a change is made on your site, you can regenerate any related transients so that when a user visits your site, they are ready to be retrieved.

In this snippet, you are going to prime a transient when a post is saved.

add_action(
    'save_post',
    function () {
        // The post was saved.
        $query = new WP_Query( array( 'posts_per_page' => 25, 'fields' => 'ids' ) );
        if ( $query->have_posts() ) {
            $persisted_data = $query->posts;
        } else {
            $persisted_data = array();
        }
        set_transient( 'dev-blog-post-id-list', $persisted_data, WEEK_IN_SECONDS );
    },
);

Storing markup

Transients don’t always have to store data. In some cases, it may be more performant to generate and store some markup. The concepts are the same, check for the transient and then regenerate the markup and save it if it doesn’t exist.

Here you are going to retrieve the five most recent posts and generate a list showing the post title and author name.

if ( false === ( $markup = get_transient( 'stored_markup' ) ) ) {
    $query  = new WP_Query( array( 'posts_per_page' => 5 ) );
    $markup = '';
    if ( $query->have_posts() ) {
        $markup .= '<ul>';
        foreach ( $query->posts as $post ) {
            $markup .= '<li>';
            $markup .= get_the_title( $post->ID ) . ' | ';
            $markup .= get_the_author_meta( 'nicename', $post->post_author );
            $markup .= '</li>';
    }
    $markup .= '</ul>';
    set_transient( 'stored_markup', $markup, WEEK_IN_SECONDS );
    }
}

Storing API responses

The responses from third-party API requests are a fantastic candidate for transients. It’s pretty rare that you would want to have every page load make a new request and in fact storing the results in a transient will mitigate any issues with rate-limiting or slow responses from the API.

In this snippet, you’re going to make a request to a third-party meme generator service and store the meme data in a transient.

if ( false === ( $api_data = get_transient( 'stored_memes' ) ) ) {

    // Make the request to the API.
    $response = wp_remote_get( 'https://api.imgflip.com/get_memes', array( 'headers' => array( 'Content-Type'  => 'application/json') ) );

    // Ensure that there is a response.
    if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) {

        // Retrieve the body of the request.
        $body = wp_remote_retrieve_body( $response );

        // Process API data.
        $api_data_json = json_decode( $body, true );
        $api_data      = $api_data_json['data']['memes'];

        // Store the results for a maximum of 1 hour.
        set_transient( 'stored_memes', $api_data, MINUTE_IN_SECONDS * 60 );
    }
}

Have you used transients in an interesting or unexpected way? If so, share your examples in the comments!

Props @greenshady and @milana_cap for review of this article.

source


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Discover more from Wordpress supported for Telkom University

Subscribe now to keep reading and get access to the full archive.

Continue reading