Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Intro to advanced caching in WordPress

maor
February 20, 2013

Intro to advanced caching in WordPress

Presented at WordCamp Jerusalem 2013 (Feb. 20, 2013)

maor

February 20, 2013
Tweet

Other Decks in Programming

Transcript

  1. Maor Chasen WP Developer @ illuminea Core contributor for 3.5

    Have plugins on WordPress.org Open Source Fanatic Design Enthusiast Flying kites
  2. 1. It's easy 2. Your site = faster 3. Can

    save you $$ 4. It will knock your socks off 5. You'll sleep better at night. Why caching is good for you? A question you shouldn't ask your mom
  3. What we will talk about • What is fragment /

    object caching • Why caching is important • How to cache • What to cache • When to cache
  4. Transients are • temporary bits of data (usually raw data)

    • like options, but w/ expiration time • always persistent out of the box • stored in wp_options by default • stored in memory if a caching backend (APC, Memcache, XCache, etc.) is available • available since 2.8
  5. • fetching data from a remote source • performing an

    expensive* query or request ◦ meta_query ◦ tax_query • data persistence is absolutely required** Use Transients when * operations that require high CPU usage or high latency (Identify slow queries with Debug Bar) ** ex. - when polling data from external sources
  6. wp_nav_menu( array( 'theme_location' => 'primary', ) ); <ul id="menu-primary" class="menu">

    <li id="menu-item-10" class="menu-item"> <a href="...">...</a> </li> ... </ul>
  7. // Get an existing copy of our transient data $menu_html

    = get_transient( 'wcj_2013_menu' ); if ( false === $menu_html ) { // data is not available, let's generate it $menu_html = wp_nav_menu( array( 'theme_location' => 'primary', 'echo' => false, ) ); set_transient( 'wcj_2013_menu', $menu_html, DAY_IN_SECONDS ); } // do something with $menu_html echo $menu_html; 60 * 60 * 24 returns the menu instead of printing
  8. // Get an existing copy of our transient data $menu_html

    = get_transient( 'wcj_2013_menu' ); if ( false === $menu_html ) { // data is not available, let's generate it $menu_html = wp_nav_menu( array( 'theme_location' => 'primary', 'echo' => false, ) ); set_transient( 'wcj_2013_menu', $menu_html ); } // do something with $menu_html echo $menu_html; doesn't expire, but it might anyway
  9. function wcj_2013_invldt_menu( $menu_id, $menu_data ) { delete_transient( 'wcj_2013_menu' ); }

    add_action( 'wp_update_nav_menu', 'wcj_2013_invldt_menu', 10, 2 ); Now, let's invalidate this action runs whenever a menu is being saved
  10. function wcj_2013_get_twitter_followers_for( $handle ) { $key = "wcj_2013_followers_$handle"; if (

    false === ( $followers_count = get_transient( $key ) ) ) { // transient expired, regenerate! $followers_count = wp_remote_retrieve_body( wp_remote_get( "http://api.twitter.com/1/users/show.json? screen_name=$handle" ) ); // request failed? if ( empty( $followers_count ) ) return false; // extract the number of followers $json = (object) json_decode( $followers_count ); $followers_count = absint( $json->followers_count ); // request was complete, store data in a transient for 6 hours set_transient( $key, $followers_count, 6 * HOUR_IN_SECONDS ); } return $followers_count; }
  11. function wcj_2013_get_twitter_followers_for( $handle ) { $key = "wcj_2013_followers_$handle"; if (

    false === ( $followers_count = get_transient( $key ) ) ) { // transient expired, regenerate! $followers_count = wp_remote_retrieve_body( wp_remote_get( "http://api.twitter.com/1/users/show.json? screen_name=$handle" ) ); // request failed? if ( empty( $followers_count ) ) return false; // extract the number of followers $json = (object) json_decode( $followers_count ); $followers_count = absint( $json->followers_count ); // request was complete, store data in a transient for 6 hours set_transient( $key, $followers_count, 6 * HOUR_IN_SECONDS ); } return $followers_count; }
  12. function wcj_2013_get_twitter_followers_for( $handle ) { $key = "wcj_2013_followers_$handle"; if (

    false === ( $followers_count = get_transient( $key ) ) ) { // transient expired, regenerate! $followers_count = wp_remote_retrieve_body( wp_remote_get( "http://api.twitter.com/1/users/show.json? screen_name=$handle" ) ); // request failed? if ( empty( $followers_count ) ) return false; // extract the number of followers $json = (object) json_decode( $followers_count ); $followers_count = absint( $json->followers_count ); // request was complete, store data in a transient for 6 hours set_transient( $key, $followers_count, 6 * HOUR_IN_SECONDS ); } return $followers_count; }
  13. function wcj_2013_get_twitter_followers_for( $handle ) { $key = "wcj_2013_followers_$handle"; if (

    false === ( $followers_count = get_transient( $key ) ) ) { // transient expired, regenerate! $followers_count = wp_remote_retrieve_body( wp_remote_get( "http://api.twitter.com/1/users/show.json? screen_name=$handle" ) ); // request failed? if ( empty( $followers_count ) ) return false; // extract the number of followers $json = (object) json_decode( $followers_count ); $followers_count = absint( $json->followers_count ); // request was complete, store data in a transient for 6 hours set_transient( $key, $followers_count, 6 * HOUR_IN_SECONDS ); } return $followers_count; } printf( 'I have %d followers!', wcj_2013_get_twitter_followers_for( 'maorh' ) );
  14. Object Cache is • non-persistent out of the box •

    stored in PHP memory by default • ideally* persistent when a caching backend is available • similar to Transients • available since 2.0 * Success depends on server environment
  15. Is post_id = 13 cached? Do something with that post

    Store results in Object Cache DB SELECT * FROM wp_posts WHERE ID = 13 Yes No Common use of the Object Cache API
  16. • wp_cache_add( $k, $d, $g, $ex ) • wp_cache_get( $k

    ) • wp_cache_set( $k, $d, $g, $ex ) • wp_cache_delete( $k, $g ) • wp_cache_incr( $k, $offset, $g ) Object Cache functions at your disposal $key, $data, $group, $expiration
  17. $song_obj = wp_cache_get( $id, 'songs' ); if ( false ===

    $song_obj ) { $song_obj = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->songs WHERE ID = %d", $id ) ); wp_cache_set( $id, $song_obj, 'songs' ); } // do something with $song_obj Example
  18. Best Practices for the best of us • prefer refreshing

    the cache ONLY when data is added/changed • Avoid, if possible, caching on front end page requests (instead, generate the data on an admin event*) • Design to fail gracefully (never assume that data is in the cache) * useful events: publish_post, transition_post_status, created_ {$taxonomy}, edited_{$taxonomy}
  19. Out of the box Memory Cache Backend Transients API persistent

    - database Persistent Object Cache API non-persistent - memory Ideally Persistent http://wordpress.stackexchange.com/a/45137 Side-by-side comparison