Purespeed: Cache bin backends

Drupal is fantastic at caching itself (I mean, you've gotta be with a code base this hefty).  Drupal 8 will see the dawn of render cache in core which is why most metrics you see of D8 are uncached (because it's basically cheating its so fast cached). If you want to play around with Render Cache in D7, it can be done but still needs some work (Drupal.org actually runs Render Cache backport to handle comments in issue queues among other things).

What we'll be focusing on here though is not the render engine but the stoage of that data. Drupal's extensive and flexible cache backend system allows you to modify where its drawing cached data from so that it doesn't have to hit mysql / the data base. The idea is that if you can push commonly requested cache bins to locations other then mysql (like RAM) then you can hit higher throughput and also reduce the time it takes to deliver a page.

Cache bin modification sounds scary, but it's actually pretty easy (though I wouldn't just go doing it blindly on production). You modify your settings.php file to include some additional configuration settings and Drupal uses the supplied project to handle the cache bin for that data. It's also the best way to confuse your Drupal friends as to why your sites are "stupid fast" on admin pages (and all pages for that matter).

Here are some different cache bin / backends you can implement (there are others):

For this post we'll focus on APC since it's the easiest to get hooked up and can be run on limited resources (I use it on everything). We'll use the APC module which uses the APC User bin (or apcu project in PHP 5.5+) to swap out the location of some cache bins. We're drawing from parts of the default ELMSLN shared_settings.php file that's included with all systems setup in the network.

To define a different cache backend drupal should know about, add something like this to your settings.php file:

$conf['cache_backends'][] = 'sites/all/modules/apc/drupal_apc_cache.inc';

This tells drupal that apc is supplying a cache backend for it to care about so that it loads this file early on in its bootstrap process.

From there, if you open up a mysql database explorer you can see all the different cache tables (all prefixed with cache_WHATEVER). To push one of these cache bins to draw from memory / APCu instead of mysql, you can add a line like the following:

$conf['cache_class_cache_WHATEVER'] = 'DrupalAPCCache';

replacing whatever with admin_menu for example, will push the cache_admin_menu bin to be delivered via memory instead of touching mysql. This bin might only get hit once, but that's 1 less time mysql is being asked to do anything. Here's some bins that I generally push into RAM / a memory based cache bin system.

$conf['cache_class_cache_admin_menu'] = 'DrupalAPCCache';
$conf['cache_class_cache_block'] = 'DrupalAPCCache';
$conf['cache_class_cache_bootstrap'] = 'DrupalAPCCache';
$conf['cache_class_cache_entity_file'] = 'DrupalAPCCache';
$conf['cache_class_cache_entity_og_membership'] = 'DrupalAPCCache';
$conf['cache_class_cache_entity_og_membership_type'] = 'DrupalAPCCache';
$conf['cache_class_cache_field'] = 'DrupalAPCCache';
$conf['cache_class_cache_menu'] = 'DrupalAPCCache';
$conf['cache_class_cache_libraries'] = 'DrupalAPCCache';
$conf['cache_class_cache_token'] = 'DrupalAPCCache';
$conf['cache_class_cache_views'] = 'DrupalAPCCache';
$conf['cache_class_cache_path_breadcrumbs'] = 'DrupalAPCCache';
$conf['cache_class_cache_path'] = 'DrupalAPCCache';
$conf['cache_class_cache_book'] = 'DrupalAPCCache';

Generally speaking you can push anything to APC cache bins and you'll start skipping mysql requests all over the place. You could just issue $conf['cache_default_class'] = 'DrupalAPCCache'; and push everything to APC but I wouldn't do this for a few reasons including..

  1. You'll fill up available APC memory quickly for sites that change often (if they don't change frequently and aren't very big then I guess you could but I still wouldn't)
  2. APC doesn't clean up after itself so when a cache expires, it just creates a new one and fragments your memory (I issue an apache restart during maintenance issue to overcome this, especially on smaller projects)
  3. Some things you can't serve from memory like the form cache or your forms submissions will constantly be marked invalid (like every stinking page submitting data)

To solve this, I always put something like this at the end of my settings files to avoid hating myself later.

# Default DB for the ones that change too frequently and are small
$conf['cache_default_class'] = 'DrupalDatabaseCache';
$conf['cache_class_cache_form'] = 'DrupalDatabaseCache';

Some ending notes:

  • If you are doing high scale sites, memcache / redis are the way to go. These will give you dedicated services for delivering cache from memory and you can push a lot of the load off to these via distributed instances. They are a lot more complex to setup though which is why I didn't cover them here.
  • Other projects like Authcache can define specific cache backends (and you'll notice both authcache and apdqc in my example settings file). These will be covered in a later post as they both offer their own complexity and take drupal even further then cache bin management alone.
  • If your running off of a newer cloud hosting cluser like Digital Ocean, Linode or others that are running on solid state drives (SSD) then you might want to look into using filecache as a storage backend. The reason being that SSD and RAM are effectively the same as far as seek time, but SSD and filecache can expire and clean up after themselves correctly. This allows you to push all (except form) bins to disk with no real negative connotation. I've experimented with this in the past with positive results. The only cavet I'll give is be careful setting up filecache as it's the only cache backend I've experienced WSOD's associated with and can be tricky to recover from unless you know what you're doing.