Slide 1

Slide 1 text

Advanced Caching An Introduction to methods in WordPress

Slide 2

Slide 2 text

Maor Chasen WP Developer @ illuminea Core contributor for 3.5 Have plugins on WordPress.org Open Source Fanatic Design Enthusiast Flying kites

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

It's not scary. It's fun.

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

What we will talk about ● What is fragment / object caching ● Why caching is important ● How to cache ● What to cache ● When to cache

Slide 7

Slide 7 text

What we won't talk about ● Server setups ● Caching plugins

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Transients API first things first

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

● 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

Slide 12

Slide 12 text

Transients functions at your disposal read core? wp-includes/option.php

Slide 13

Slide 13 text

get_transient( $transient ); Get it. unique string (key) representing a piece of data

Slide 14

Slide 14 text

set_transient( $transient, $value, $expiration = 0 ); Set it. the data you wish to store for how long?

Slide 15

Slide 15 text

delete_transient( $transient ); Scrap it. unique string (key) representing a piece of data

Slide 16

Slide 16 text

get_site_transient(); set_site_transient(); delete_site_transient(); Multisite? You're in luck! All Transients functions have their network-wide counterparts.

Slide 17

Slide 17 text

Use case: Using Transients to store fragmented data

Slide 18

Slide 18 text

wp_nav_menu( array( 'theme_location' => 'primary', ) );

Slide 19

Slide 19 text

Without using wp_nav_menu() After using wp_nav_menu() That is ~5 extra queries (for each menu)

Slide 20

Slide 20 text

Instead, we can cache the menu in a transient and save on DB queries

Slide 21

Slide 21 text

// 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

Slide 22

Slide 22 text

// 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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Use Transients when fetching data from a remote source http://... https://...

Slide 25

Slide 25 text

Twitter API Facebook API Google+ API Pick your poison last.fm API

Slide 26

Slide 26 text

Example (Twitter API): Retrieve follower count and cache the results

Slide 27

Slide 27 text

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; }

Slide 28

Slide 28 text

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; }

Slide 29

Slide 29 text

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; }

Slide 30

Slide 30 text

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' ) );

Slide 31

Slide 31 text

Let's spice it up Got some metrics!

Slide 32

Slide 32 text

Without Transients 0.36014295 seconds WITH Transients 0.00010109 seconds

Slide 33

Slide 33 text

That is 3,562 X FASTER

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

Object Cache API last but not least

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Object Cache functions at your disposal read core? wp-includes/cache.php

Slide 39

Slide 39 text

● 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

Slide 40

Slide 40 text

$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

Slide 41

Slide 41 text

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}

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

Questions? Thanks! @maorh keep in touch