Cache Money Business
Mark Jaquith“JAKE-with”
@markjaquith [email protected] markjaquith.com
@markjaquith #wcldn
@markjaquith #wcldn
@markjaquith #wcldn
@markjaquith #wcldn
$CA£ING@markjaquith #wcldn
Caching storing data for future use
@markjaquith #wcldn
More Cache Fewer Problems
@markjaquith #wcldn
Caching Principles
@markjaquith #wcldn
DO LESS WORK
@markjaquith #wcldn
DO LESS WORK
@markjaquith #wcldn
WORK LESS OFTE
N
WORK LESS OFTEN
$TTL = 5400; $TTL = 900;
@markjaquith #wcldn
MAKE GENERIC OUTPUT
@markjaquith #wcldn
Cache the whole page
@markjaquith #wcldn
Batcache
@markjaquith #wcldn
W3 Total Cache
@markjaquith #wcldn
WP Super Cache
@markjaquith #wcldn
Varnish or Nginx
@markjaquith #wcldn
CDN —
Content Distribution
Network
@markjaquith #wcldn
Nginx Cache Purging
labs.frickle.com/nginx_ngx_cache_purge/
@markjaquith #wcldn
location ~ \.php$ {
}
@markjaquith #wcldn
set $no_cache_set 0;set $no_cache_get 0;
@markjaquith #wcldn
if ( $http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) { set $no_cache_set 1; set $no_cache_get 1;}
@markjaquith #wcldn
# X-Nginx-Cache-Purge: 1# header will skip the cache!
if ( $http_x_nginx_cache_purge ) { set $no_cache_get 1;}
@markjaquith #wcldn
fastcgi_no_cache $no_cache_set;
fastcgi_cache_bypass $no_cache_get;
@markjaquith #wcldn
function nginx_purge_url( $url ) { wp_remote_get( $url, array( 'reject_unsafe_urls' => false, 'timeout' => 0.01, 'blocking' => false, 'headers' => array( 'X-Nginx-Cache-Purge' => '1' ) ));}
@markjaquith #wcldn
add_action( 'transition_post_status', 'nginx_purge_post', 10, 3);
function nginx_purge_post( $new, $old, $post ) { if ( 'publish' === $old || 'publish' === $new ) {
nginx_purge_url( get_permalink( $post ) ); }}
@markjaquith #wcldn
Variable cache lengths +
Proactive cache refreshing
@markjaquith #wcldn
add_action( 'template_redirect', 'cache_home_page_length);
function cache_home_page_length() { if ( ! is_user_logged_in() && is_home() ) { header( 'Cache-Control: public, max-age=300' ); }}
@markjaquith #wcldn
Logged in = no page caching?
@markjaquith #wcldn
¥€$@markjaquith #wcldn
How does the page vary?
@markjaquith #wcldn
1. Toolbar 2. Inline edit links 3. User's private posts 4. Moderated comments shown 5. Comment form
@markjaquith #wcldn
Remove these things —OR—
Make them generic
@markjaquith #wcldn
Comment form HIDE
@markjaquith #wcldn
Toolbar GONE
@markjaquith #wcldn
Inline edit links GONE
@markjaquith #wcldn
User's private posts GONE
@markjaquith #wcldn
Moderated comments GONE
@markjaquith #wcldn
Compare:
10 editors and 1000 subscribers view 5 pages
each
@markjaquith #wcldn
Caching skipped for logged in users:
5,050 dynamic views
@markjaquith #wcldn
Caching skipped for Editors and up only:
50 dynamic views 5000 cached views
@markjaquith #wcldn
Less than 1% of the workload as before!
@markjaquith #wcldn
What if the page really DOES need per user
customization?
e.g. "Howdy $username"
@markjaquith #wcldn
1. Load 2. Ajax if logged in 3. Customize
@markjaquith #wcldn
Um… cookies
@markjaquith #wcldn
1. Set JS-readable cookies 2. Get rid of the normal
ones 3. Populate the form with
JavaScript
@markjaquith #wcldn
Good news…
@markjaquith #wcldn
Cache Buddy github.com/markjaquith/cache-buddy
@markjaquith #wcldn
Cache WordPress
Objects
@markjaquith #wcldn
APCu (single server only)
— Memcache
— Redis
@markjaquith #wcldn
APCu WordPress.org/plugins/apc
— Memcache
WordPress.org/plugins/memcached
— Redis
WordPress.org/plugins/wp-redis
@markjaquith #wcldn
wp-content/object-cache.php
@markjaquith #wcldn
W3 Total Cache +
Batcache
@markjaquith #wcldn
Using Transients and the Object Cache
@markjaquith #wcldn
Transients vs
Object Caching
@markjaquith #wcldn
Transients use object caching, if available
Otherwise, the options table
@markjaquith #wcldn
The Object Cache API uses an object caching
backend, if available
Otherwise, in-process PHP memory
@markjaquith #wcldn
Transient API set_transient( $key, $data, $ttl )
get_transient( $key )
@markjaquith #wcldn
Object Cache API wp_cache_get( $key, $group )
wp_cache_set( $key, $data, $group, $ttl )
@markjaquith #wcldn
Avoiding Cache Stampedes and
Cold Caches
@markjaquith #wcldn
$item = get_transient('footer_item');if ( ! $item ) { // Some poor person has to WAIT $get = wp_remote_get( get_option( 'footer_url' ) ); if ( ! is_wp_error( $get ) ) { $item = wp_remote_retrieve_body( $get ); wp_set_transient( 'footer_item', $item, 300 ); } }echo $item;
@markjaquith #wcldn
TLC Transients —
bit.ly/tlc-transients
@markjaquith #wcldn
function fetch_footer_item() { $get = wp_remote_get( get_option( 'footer_url' ) ); if ( ! is_wp_error( $get ) ) { return wp_remote_retrieve_body( $get ); } else { return false; }}
echo tlc_transient('footer_item') ->update_with('fetch_footer_item') ->expires_in(300) ->background_only() ->get();
@markjaquith #wcldn
Cache HTML Fragments
— bit.ly/fragment-cache
@markjaquith #wcldn
$fragment = new CWS_Fragment_Cache( 'unique-key', 3600);
if ( ! $fragment->output() ) { // START functions_that_do_stuff_live(); these_should_echo(); // END $fragment->store();}
@markjaquith #wcldn
In review
@markjaquith #wcldn
Caching for speed —
Caching for scale
@markjaquith #wcldn
Less work —
Generic output
@markjaquith #wcldn
Cache the whole page
@markjaquith #wcldn
Think Nginx and Varnish, not PHP caching solutions
@markjaquith #wcldn
Check out my whacky new plugin
github.com/markjaquith/cache-buddy
@markjaquith #wcldn
Use Object Caching
@markjaquith #wcldn
Leverage Transients (or maybe the Object Cache directly)
@markjaquith #wcldn
Stampedes are bad
@markjaquith #wcldn
Cache expensive, reusable HTML
fragments
@markjaquith #wcldn
Q A(if we have time, which we probably do not)
&@markjaquith #wcldn