Transient not working for custom loops

I’m working at a WordPress theme. Since I need to display featured posts, related posts, some widgets with recent posts and so on, I need to use multiple custom loops. Because of this, the number of database queries have also increased.

In an attempt to optimize the theme for better performance I came across http://codex.wordpress.org/Transients_API , which seems like a good way to cache the loops that I don’t need to be updated to every page reload.

So far so good, I used transient for wp_nav_menu and it worked as it should. I managed to decrease a bit the number of db queries.

The problem occured when I tried to use the transient for custom loops.

The transient is saved.
I can get the transient value.
The only problem is that the number of the database queries seems to be significantly higher than when using WP_Query without a transient.

Here is my custom loop:

if( false === ( $loop = get_transient('featured') ) ) {
    $loop = new WP_Query( array( 'posts_per_page' => 20 ) );
    set_transient('featured', $loop, 60 * MINUTE_IN_SECONDS);
}

if( $loop->have_posts() ) :

    while( $loop->have_posts() ) : $loop->the_post();

        the_title();
        the_post_thumbnail('thumb');

    endwhile;

endif;
wp_reset_postdata();

The code used to display the number of database queries and execution time is the following:

<?php echo get_num_queries(); ?> queries in <?php timer_stop(1); ?> seconds.

Using a query debug plugin, I see that update_meta_cache() generates a lot of queries.

I can’t explain why the number of database queries increases instead of decreasing and why this worked for menues, but not for custom loops. Maybe I missed something. Any help is appreciated.

2 Answers
2

I have actually worked on a post yesterday (check it out here) and was hit by the same problem. I’m also new to the Transient API, never actually worked with it :-).

The problem here is that for a specific loop you need to use a certain value outside of your transient. You are simply storing the wrong values, and because of this, you are getting these overload of db hits.

To explain, here is part of the code from the answer I’ve referred to. I need to create a list of categories with post titles belonging to these categories. To accomplish this, I run my custom query and then use the value of $q to create my list

    $args = array( 
        'posts_per_page' => -1
    );

    $query = new WP_Query($args); 

    $q = array();

    while ( $query->have_posts() ) { 

        $query->the_post(); 

        $a="<a href="". get_permalink() .'">' . get_the_title() .'</a>';

        $categories = get_the_category();

        foreach ( $categories as $key=>$category ) {

            $b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';    

        }

        $q[$b][] = $a; // Create an array with the category names and post titles
    }


    /* Restore original Post Data */
    wp_reset_postdata();

This gives me 6 queries in 0.06348 seconds.

OK, if you look at what the example from the codex, you should put your new WP_Query result in a transient. If I do that, the results is totally haywire

<?php
if( false === ( $query = get_transient('custom_query') ) ) {
    $args = array( 
        'posts_per_page' => -1
    );

    $query = new WP_Query($args); 
 set_transient('custom_query', $query, 60 * MINUTE_IN_SECONDS);
}
    $q = array();

    while ( $query->have_posts() ) { 

        $query->the_post(); 

        $a="<a href="". get_permalink() .'">' . get_the_title() .'</a>';

        $categories = get_the_category();

        foreach ( $categories as $key=>$category ) {

            $b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';    

        }

        $q[$b][] = $a; // Create an array with the category names and post titles
    }


    /* Restore original Post Data */
    wp_reset_postdata();


?>

With the code above, my results looks like this 31 queries in 0.19141 seconds. This is 25 queries more and takes about 0.13 seconds more. The reason for this is, for each post, WordPress needs to revisit the db to retrieve the categories the post belongs to, as this is not stored in you transient. 25 extra queries is the amount of posts in my db. So, you see, you have actually wasted a lot of resources by storing the wrong info. I do not need the info from the new WP_Query, but the result/value from $q

So, to properly make use of transients, you have to store the correct values you are after. To do this, I need to add my complete query into a transient to make sure that it is only run once, and that is when the transient is created. After that, the custom query is not needed anymore and is redundant, the only thing that needs to be stored and that I’m after now is the value $q. So, this is how the query is altered to remove the custom query after the transient is created, and to save just the value of $q

Just to note, I’ve changed my transient names along the way just for testing purposes to show the times and queries. Also note that when creating your transient, you need to use the value (in this case $q) you need to save

 if ( false === ( $q = get_transient( 'category_list' ) ) ) {

    $args = array( 
        'posts_per_page' => -1
    );

    $query = new WP_Query($args); 

    $q = array();

    while ( $query->have_posts() ) { 

        $query->the_post(); 

        $a="<a href="". get_permalink() .'">' . get_the_title() .'</a>';

        $categories = get_the_category();

        foreach ( $categories as $key=>$category ) {

            $b = '<a href="' . get_category_link( $category ) . '">' . $category->name . '</a>';    

        }

        $q[$b][] = $a; // Create an array with the category names and post titles
    }


    /* Restore original Post Data */
    wp_reset_postdata();

set_transient( 'category_list', $q, 12 * HOUR_IN_SECONDS );
}

This give me 2 queries in 0.00195 seconds. You see how I have now effectively made use of transients. $q which is an array of many values` is now available to create my list, and it only cost me 2 db hits to accomplish my goal

I hope this all makes sense

Leave a Comment