Using Memcached with Rails
Recently, during the development of BoxFile, I decided that I needed a nice caching layer on the site to alleviate unnecessary load on the server, but to also speed the application up on some of the more intensive pages such as the document view. After a couple of minutes research, it transpired in Rails that this is actually pretty easy.
Enter Memcached (pronounced memcache-dee). Essentially Memcached is a heap of key value pairs stored in a nice high performance service that available over the network. Given that memcached is cluster-able, this means that you can practically have a infinitely big network cache for your application, which is great news.
However, most apps don't need anything like that amount of stuff, a cache of a few megabytes is ideal (and the heroku memcache makes it insanely easy to setup too).
But how do you use it?
Well, it's as easy as you could imagine.
Let's say I have a bit of view that I want to cache that contains a render of a blog post article. In order to cache it with memcached, I simply need to install the memcached gem, tell Rails I'm using Memcached for my cache store,
and then mark out what needs caching with a block:
What Rails does here is spot the @post object, and generate a key based off that and it's updated date. This then forms the key that gets used in the cache. When the post changes, so does the key, which means no cache item is found and the item with re-generate and cache itself in the process.
Let's say for instance that the view thats, say, specific to a user in it:
works just as well, as does
Basically, if you can generate a unique key then that cache item can be found again. If you need a new version, use a new key (hence the use of updated dates), and Memcached will automatically worry about expiring the old fragment when it needs to.
However, note that there's a catch in Rails 2.x. If you're caching a view, remember that any code in your controllers will run regardless as the SQL itself will be cached.
There's a few ways around this given that Memcached doesn't only store Strings, but also hashes and so on. ActionController::Caching::Fragments is your friend here.
In Rails 3.x this isn't an issue as queries are only executed by ActiveRecord when the query result is iterated over (and if it's cached, this won't happen)
So, all in all, it's pretty simple this caching game, to the point where I was able to implement all the caching I need for now in an hour or so, and whats more, I know it'll scale, unlike file system or database based caches. For proof, read up on the Facebook implementation.
For more information, check out the screencast over on NewRelic.