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