Caching for Coders:
How, What, and When to Cache in WordPress

Zack Tollman, 10up LLC

@Zack_Dev, 10up.com

Slides: tollmanz.github.com/caching-for-coders

Conversation: github.com/tollmanz/caching-for-coders

Metaphor

Source: http://toblender.com/memcached-story-part-1/

Metaphor

What We Are Talking About

How to Cache

Transients

function zt_display_leaders() {
    $leaders = get_transient( 'zt-leaders' );
    if ( false === $leaders ) {
        $leaders = zt_get_leaders();
        set_transient( 'zt-leaders', $leaders, 300 );
    }

    return $leaders;
}

How to Cache

WP_Object_Cache

function zt_display_leaders() {
    $leaders = wp_cache_get( 'zt-leaders', 'zt-stats' );
    if ( false === $leaders ) {
        $leaders = zt_get_leaders();
        wp_cache_set( 'zt-leaders', $leaders, 'zt-stats', 300 );
    }

    return $leaders;
}

Discussion of some differences by Otto

What to Cache

When to Cache

Gameplan

When to Cache — A Common Solution

Cache when the data is not cached

// functions.php
function zt_get_tweet() {
    if ( false === ( $tweet = get_transient( 'zt-tweet' ) ) ) {
        $tweet = zt_request_tweet(); // Talk to Twitter
        set_transient( 'zt-tweet', $tweet, 300 );
    }
    return $tweet;
}

// sidebar.php
<h3>Latest Tweet:</h3>
<p><?php echo zt_get_tweet(); ?></p>

When to Cache — When the Data Changes

When to Cache — When the Data Changes

function zt_set_best_games( $post_id, $post ) {
    if ( 'zt-game' != $post->post_type )
        return false;

    zt_determine_best_games();
}

add_action( 'publish_post', 'zt_set_best_games', 99, 2 );

function zt_determine_best_games() {
    /* Get best reviewed games; set to $top_games */
    set_transient( 'zt-top-games', $top_games );
    return $top_games;
}

In 2 years

1 post per day: 730 runs

Common solution: 210,240 runs

When to Cache — On an Interval

When to Cache — On an Interval

function zt_schedule_tweet_retrieval() {
    if ( ! wp_next_scheduled( 'zt_tweet_sched' ) )
        wp_schedule_event( time(), 'hourly', 'zt_tweet_sched' );
    add_action( 'zt_tweet_sched', 'zt_request_tweet' );
}
add_action( 'init', 'zt_schedule_tweet_retrieval' );

function zt_request_tweet() {
    // Talk to Twitter
    set_transient( 'zt-tweet', $tweet );
    return $tweet;
}

function zt_get_tweet() {
    if ( false === ( $tweet = get_transient( 'zt-tweet' ) ) ) {
        $tweet = zt_request_tweet(); // Talk to Twitter
        set_transient( 'zt-tweet', $tweet );
    }
    return $tweet;
}

When to Cache — Failing Gracefully

When to Cache — Failing Gracefully

Placeholder text

function zt_display_tweet() {
    if ( $tweet = get_transient( 'zt-tweet' ) ) {
        return $tweet;
    else
        return 'Latest Tweet Unavailable';
}

Regenerate data

function zt_display_tweet() {
    if ( $tweet = get_transient( 'zt-tweet' ) ) {
        return $tweet;
    else
        return zt_get_tweet(); // Talk to Twitter
}

When to Cache — Failing Gracefully

Storing a "backup"

function zt_schedule_tweet_retrieval() {
    if ( ! wp_next_scheduled( 'zt_tweet_sched' ) )
        wp_schedule_event( time(), 'hourly', 'zt_tweet_sched' );
    add_action( 'zt_tweet_sched', 'zt_generate_tweet' );
}

add_action( 'init', 'zt_schedule_tweet_retrieval' );

function zt_generate_tweet() {
    $tweet = zt_request_tweet(); // Talk to Twitter
    set_transient( 'zt-tweet', $tweet );

    if ( ! get_option( 'zt-tweet' ) )
        add_option( 'zt-tweet', $tweet, '', 'no' );
    else
        update_option( 'zt-tweet', $tweet );

    return $tweet;
}

When to Cache — Failing Gracefully

Using the "backup"

function zt_display_tweet() {
    if ( $tweet = get_transient( 'zt-tweet' ) ) {
        return $tweet;
    } elseif ( $tweet = get_option( 'zt-tweet' ) {
        set_transient( 'zt-tweet', $tweet );
        return $tweet;
    } else {
        return zt_generate_tweet();
    }
}

When to Cache — Avoid Front End Caching

Beer List

function zt_schedule_generate_beer() {
    if ( ! wp_next_scheduled( 'zt_beer_sched' ) )
        wp_schedule_event( time(), 'daily', 'zt_beer_sched' );
    add_action( 'zt_beer_sched', 'zt_generate_beer_list' );
}
add_action( 'init', 'zt_schedule_generate_beer' );

function zt_generate_beer_list() {
    // Query API and generate HTML
    $beer_list = zt_get_beer_list();
    set_transient( 'zt-beers', $beer_list );
    delete_transient( 'zt-beer-sched-single' );
    return $beer_list;
}

function zt_generate_beer_on_publish( $post_id, $post ) {
    zt_generate_beer_list();
}
add_action( 'publish_post', 'zt_generate_beer_on_publish', 10, 2 );

Beer List

// Same function from previous slide
function zt_generate_beer_list() {
    // Query API and generate HTML
    $beer_list = zt_get_beer_list();
    set_transient( 'zt-beers', $beer_list );
    delete_transient( 'zt-beer-sched-single' );
    return $beer_list;
}

function zt_display_all_the_beers() {
    if ( $beers = get_transient( 'zt-beers' ) ) {
        return $beers;
    } else {
        if ( ! get_transient( 'zt-beer-sched-single' ) ) {
            set_transient( 'zt-beer-sched-single', 'yes' );
            wp_schedule_single_event( time(), 'zt_beer_sched' );
        }
        return 'Beer list is being generated.';
    }
}

Beer List

Mission Brewery

  • Shipwrecked Double IPA
  • Amber Ale
  • Blonde Ale
  • Hefeweizen
  • IPA

The Beer Company

  • Beer list not available.

Butcher's Brewing Company

  • Mucho Aloha HPA
  • Mucho Aloha Imperial IPA
  • Nut Brown Ale
  • Pig's Premium Nut Brown

Coronado Brewing Company

  • Islander IPA
  • Point Loma
  • Uptown Brown
  • Mermaid Red
  • Four Brothers Pale Ale
  • Coronado Golden Ale
  • Mermaids Red Ale
  • Bugger Brown
  • Outlet Stout
  • Idiot IPA
  • Islandweizen
  • Black Ops IPA
  • Frog's Breath IPA
  • Hoppy Daze
  • Black Pearl
  • Hop Scotch
  • North Island
  • Saison By the Sea
  • Stoopid Stout
  • Down N out
  • Barrel Aged Barley Wine Ale

Thank You

Andrew Nacin (@nacin)

Daniel Bachhuber (@danielbachhuber)

Helen Hou-Sandi (@helenhousandi)

Laura Milewski (@l

Steve Locker (@slocker3)