Today I’m starting a new blog series about WordPress development patterns. I’ve been working in several client projects lately, mainly custom theme development, and I’ve noticed I’m using over and over the same patterns. These are the patterns I’d love to share with you.

The caching pattern

According to the best practices, the complex queries should be cached in order to improve drastically the performance of our application or website. In the WordPress world this would mean to use the WP_Object_Cache or the Transients API, but for now let’s stick with the former.

The trivial pattern is built like follows:

function my_complex_query( $refresh = false ) {
	$cache_key = 'my_result';
	if ( $refresh || false === ( $results = wp_cache_get( $cache_key ) ) ) {
		$query_args = array(); // define my query args 
		$results = new WP_Query( $query_args );
		wp_cache_set( $cache_key, $results, 'my_group', DAY_IN_SECONDS );
	return $results;

This pattern ensures we check for the existence of a cached object and if not found or expired, we are ready to generate it again.

Note I defined an expiration time using the core constant DAY_IN_SECONDS. This is also according to the good practice:

— Always set an expiration value for every object that is cached.

But what happens if a queried content type changes during this period ( e.g., a new post is created or deleted)?

To prevent an obsoleted cache object we need to find a condition to flush the cache, and that usually is made hooking on the save_post action, when we’re querying for posts or post types:

function my_theme_flush_cache( $post_id ) {
	if( 'my_post_type' == get_post_type( $post_id ) ) {
		$cache_key = 'my_result';
		wp_cache_delete( $cache_key, 'my_group' );
add_action( 'save_post', 'my_theme_flush_cache' );

Depending on the scenario it could be better to re-generate the cache instead of deleting it, by calling my_complex_query( true );, as also suggested on the 10up  engineering team:

[…] the cache rebuild in this example would always be triggered by a visitor who would hit a stale cache, which will increase the page load time for the visitors and under high-traffic conditions. This can cause race conditions when a lot of people hit a stale cache for a complex query at the same time. In the worst case, this could cause queries at the database server to pile up causing replication, lag, or worse.

Now, imagine the complex query depends on multiple external parameters and we need to cache it in different objects. This might be the case for a query of posts on a certain category or depending on a certain custom field, or a combination of both. We then need to cache all the query possibilities in different objects, one per category or custom field value.

In this case, the trivial flush condition is very difficult to define, as sometimes we don’t even know the set of possible values for the external parameter.

We need a different pattern!

The last_changed pattern

Diving into the WordPress core made me learn about this pattern. It is used when the function get_comments(), is called (or its cousins). It caches the list of comments depending on the query args. This means it would be almost impossible to try to delete all the combination of cached objects if a new comment was created.

The idea behind this pattern is to add a timetstamp to the cache key to mark the last change to a post (or any other object type) targeted in the query we’re caching. We could get this timestamp using a core function wp_cache_get_last_changed to get it, as in the following example:

By adding the timestamp “last_changed” to the cache key we then only need to burn a new last_changed timestamp whenever the content changes. We could do it per post type, or per taxonomy, or user or any other core WordPress object type. A new timestamp will immediately means that the old cached object is obsolete and won’t be used on the next query.
If you’d like to read more about caching in WordPress I recommend the great article by Zack Tollman about Core Caching Concepts in WordPress.