This slideshow could not be started. Try refreshing the page or viewing it in another browser.
Caching; For Fun & Profit
Understanding different caching tools and techniques available to WordPress developers such as the Transient and Object Caching APIs and how/why they can make or break your site.
About me: Mohammad (Mo) Jangda
mo@automattic.com | batmoo@gmail.com | @mjangda
- Toronto, ON
- Code Wrangler at WordPress.com VIP (Automattic)
- Core Contributor
- Ice Cream Fan
Hiring, Hiring, Hiring!
- VIP Wranglers, VIP Wranglers, VIP Wranglers!
- Code Wranglers, Code Wranglers, Code Wranglers!
- Designers, Designers, Designers!
- Happiness Engineers, Happiness Engineers, Happiness Engineers!
What is caching?
Caching is more than just installing a plugin to fix your uptime problems.
It’s a way to temporarily store data so that it can be reused.
What is caching?
It’s meant to avoid doing the same expensive computations (fetching from the database, making remote calls, etc.) over and over again.
Put something in a nearby place so can access it more readily.
Can cache all types of things: objects, arrays, strings, integers, etc.
Analogy: A Fireplace, A Lumberjack, and A Pile o’ Wood
So What?
According to Google:
High performance web sites lead to higher visitor engagement, retention and conversions
Translation:
Fast sites == $$$
What is a slow pageload?
A profit killer.
What is a slow pageload?
Inevitable (as your traffic and site grows).
What is a slow pageload?
An insult to your users. Your users appreciate/want/need/deserve a fast site.
Goal: This, or better
The Super Secret Technique To A Really Fast Site
The Fallback: Caching
Caching: More than just for speed
Caching will help prevent your site from going down if you rely on external services. If Twitter goes down, your site can too.
Caching: More than just for speed
Will force you to think about and write better code. And become a better developer.
The Cache Loop
Most caching interactions follow a simple loop-based pattern:
- Get the value we want from cache!
- Did we get it?
- If “no”: get or generate the value and cache it!
- Use the value!
Other patterns exist, but is the simplest, most common one.
The Cache Loop
value = get from cache if ( value not found ) { generate value save value in cache } do stuff with value
Different types of caching
- Variable/static caching
- Object caching
- Fragment caching (pieces of rendered HTML or output for a page)
- Opcode caching (PHP Acceleration)
- Full-page caching (the whole rendered page)
- Asset Caching (CDN)
Full-page caching
Your first and easiest line of defence. Store the entire generated page in cache and serve to users when they visit.
* Super Cache
* W3 Total Cache
* Batcache (used on WP.com; pages served in 0.004 seconds)
Harder now that we have more personalized, user-centric sites.
The quality of your code will impact how effective a full-page cache is
Code Quality is important
Caching/optimizing at the application-level is super important.
Will touch on this later.
Opcode Caching
APC, XCache, Zend Optimizer+, etc.
Improves the actual PHP-level load times of your application/site by storing compiled versions of your PHP files.
Can significantly improve load times if your site has thousands of files (or using large plugins like BuddyPress).
Variable Assignments
Is this better?
if ( get_the_terms() ) { foreach ( get_the_terms() as $term ) { … } }
Or this?
$terms = get_the_terms(); if ( $terms ) { foreach ( $terms as $term ) { … } }
Variable Assignments
Variable assignments are a type of caching.
You store a value in memory so it can be re-used.
Caching with the static
var
Useful if you repeat the same function over and over again but don’t want the value to persist between pageloads.
function x_is_it_true() { static $is_it_true; if ( ! isset( $is_it_true ) ) $is_it_true = call_really_expensive_function(); return $is_it_true; }
Little did you know…
WordPress has caching built-in!
WordPress has caching built-in!
A built-in, non-persistent caching API.
On any given pageload, you’re going to to need to fetch a single option potentially tens of times. And WordPress tries to optimize for that.
Note: it is non-persistant!*
Here’s how WordPress caches options:
-
Fetch all
autoload = yes
options:SELECT option_name, option_value FROM wptrunk_options WHERE autoload = 'yes'
- Add the results to object cache (which is really just a global array)
- Next time an option is needed, look in the cache first
- If not found, grab from the database and update the array.
Code Snippet from get_option()
$alloptions = wp_load_alloptions(); // wp_load_alloptions fetches autoload = yes if ( isset( $alloptions[$option] ) ) { $value = $alloptions[$option]; } else { ... $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) ); ... }
WordPress caches lots of things for you!
* options
* posts
* users
* queries
* terms
* etc.
Object Caching API
This is the API WordPress uses and makes available to developers:
/** * Retrieves the cache contents from the cache by key and group. * * @param int|string $key What the contents in the cache are called * @param string $group Where the cache contents are grouped * * @return bool|mixed False on failure to retrieve contents or the cache * contents on success */ function wp_cache_get( $key, $group = '' ) {
Object Caching API
/** * Saves the data to the cache. * * @param int|string $key What to call the contents in the cache * @param mixed $data The contents to store in the cache * @param string $group Where to group the cache contents * @param int $expire When to expire the cache contents * * @return bool False on failure, true on success */ function wp_cache_set( $key, $data, $group = '', $expire = 0 ) {
Object Caching API
/** * Adds data to the cache, if the cache key doesn't already exist. * * @param int|string $key The cache key to use for retrieval later * @param mixed $data The data to add to the cache store * @param string $group The group to add the cache to * @param int $expire When the cache data should be expired * * @return bool False if cache key and group already exist, true on success */ function wp_cache_add( $key, $data, $group = '', $expire = 0 ) {
Object Caching API
/** * Removes the cache contents matching key and group. * * @param int|string $key What the contents in the cache are called * @param string $group Where the cache contents are grouped * @return bool True on successful removal, false on failure */ function wp_cache_delete( $key, $group = '' ) {
Difference between wp_cache_add
and wp_cache_set
?
If the value already exists, add
will bail; set
will override.
A few other intricacies as well, although, don’t worry about them unless you’re building really big sites.
The Cache Loop: In Action!
// First, check to see if the value is in the cache $featured_posts = wp_cache_get( 'my-featured-posts' ); // Did we find it? if ( false === $featured_posts ) { // Nope! Let's generate and cache it! $featured_posts = get_posts( array( 'post__in' => get_option( 'sticky_posts' ) ) ); wp_cache_set( 'my-featured-posts', $featured_posts ); } // Cool, now we have our value; let's use it! foreach ( $featured_posts as $post ) { ...
Warning: Careful with the false
and error conditions!
You should account for those or be prepared for pain during failures.
$tweets = my_get_twitter_tweets( '#yolo' ); if ( $tweets ) { wp_cache_set( 'yolo', $tweets, 'my-tweets' ); }
Better Handling Error Conditions
$tweets = my_get_twitter_tweets( '#yolo' ); if ( $tweets ) { wp_cache_set( 'yolo', $tweets, 'my-tweets', MINUTE_IN_SECONDS * 10 ); } else { wp_cache_set( 'yolo', array(), 'my-tweets', MINUTE_IN_SECONDS * 1 ); }
Warning: Prefixing rules still apply
If you run into conflicts, you’re gonna have a bad time!
Warning: By default, caching in WordPress in non-persistent.
This means that your cache is emptied and re-built every time a new page is loaded.
Persistent Object Caching
You can make the data persist using an object caching backend like memcached:
http://wordpress.org/plugins/memcached/
This will retain objects in cache across pageloads, so you don’t need to hit the database again.
There are other caching backends as well like APC.
If you don’t have access to object cache…
Transients
If you’re a plugin/theme author, there’s no guarantee of the environment your users are in and whether persistent object caching is available.
WordPress Transients API helps you cache things without needed a caching backend.
Transients
The data:
a) persists across pageloads; and
b) expires after a set period of time (hence the name).
Useful for things like remote data or data that you know has a limited time span.
The Transients API
function get_transient( $transient ) {} function set_transient( $transient, $value, $expiration ) {} function delete_transient( $transient ) {}
The Cache Loop: In Action!
// First, check to see if the value is in the cache $remote_data = get_transient( 'my-remote-data' ); // Did we find it? if ( false === $remote_data ) { // Nope! Let's fetch and cache it! $response = wp_remote_get( 'http://foo.com/file.txt' ); $remote_data = json_decode( wp_remote_retrieve_body( $response ) ); set_transient( 'my-remote-data', $remote_data ); } // Cool, now we have our value; let's use it! foreach ( (array) $remote_data as $tweet ) { ...
Warning: Don’t overuse transients
* they’re stored in the options table and can bloat; and
* they require database calls to fetch.
Famous Last Words
“Oh, don’t worry. I’ll just put it in cache/transient…”
I worry. A lot.
Caching helps but…
You need to be smart about and understand what your application is doing.
A slow query will always be slow regardless of how many layers of caching you add around it.
Cache Stampedes Will Break You
Cache stampede: when a load of requests in succession kick of the same expensive process because all of them hit an empty cache on or around the same time.
Too many lumberjacks chopping the same wood
Cache Stampede: Solutions
- Locking: Prevent the pileup
- Async: Separate the cache generation process
…OR FIX (REMOVE) THE DAMN THING!
The Uncached (“cold cache”) Pageload
Optimize for the worst case scenario: aim for the best by optimizing for the worst (although, within realistic means).
Uncached pageloads should be as slim as possible.
The Uncached (“cold cache”) Pageload
Technique: Kill your object cache and examine load time patterns on a few different types of pages:
- homepage
- category
- single
- 404
- any special or complex templates
Examine the Uncached Pageload
What’s the load time like?
- If it times out, that’s a red flag.
- If the pageload time is inconsistent, that’s a red flag.
- If the pageload time is unusually high, that’s a red flag.
Summary
- Caching in WordPress is “easy”: many things built-in; many ways to do it
- Understanding the intricacies of who, what, when, where to cache is hard!
- There’s a lot more to caching than I’ve covered here!
- If used well, caching will help keep your site alive, fast, and $$$!
One thought on “Presentation: Caching; for fun and profit”
Leave a Reply Cancel reply
This site uses Akismet to reduce spam. Learn how your comment data is processed.
Pingback: Day 1 at WordCamp Toronto 2013 |