37signals posted a few blog posts about how they made their upcoming Basecamp (Next) product so blazingly fast.
Amongst other things, an important factor is using their caches to THE MAX (as @dhh puts it). Which basically means: cache every representation of some state (a to do item, a todo list, a project page, etc).
The technique they are using is key-based cache expiration. Meaning, that you keep caching every fragment based on the updated_at timestamp of the object that might change.
With this comes an advantage: you will never have to expire caches since your views will always look for the fragment with the most recent updated_at timestamp of the underlying model. Your memcached server will then take care of expiring by popping the oldest key out if memory runs out.
It's really that simple
So just to illustrate how simple it is, I wanted to show you how I implemented this today in the custom-build blog engine we're using to host some of our own and other people's blogs. This is an app that runs on Heroku and you are looking at it right now.
Every post you see on this blog is a fragment that gets cached using the free 5mb memcached addon from Heroku.
To install the addon, run the following command in your app:
heroku addons:add memcache
It is recommended to use the Dalli gem for using the memcache server in your app. So add the gem to your Gemfile:
And add this to your config/environments/production.rb so Rails knows you're using Dalli for the cache:
config.cache_store = :dalli_store
For all the posts on the blog I have a _posts.html.erb partial that renders a single post. So, in that partial I can include the line:
<%= cache post do %>
<h1><%= post.title %></h1>
... etc ...
<% end %>
The cache method will use a key like "posts/3-20071224150000" for the object based on the updatedat method. Under the hood, the *cachekey* method is used.
So, when I would fix a typo in the post the updated_at column will have been updated, hence the cache key will also be updated and the cache will generate the updated post partial with the new cache key.
But what if I change the contents of the partial?
If you change the actual code in the partial the posts that are already in the database won't have a new updated_at so they will keep using the cached old generated partial.
I am currently solving this by adding a prefix to the cache key like so:
<%= cache ["v2", post] do %>
<h2><%= post.title %></h2>
<% end %>
This way the next time someone opens a page with posts, the new partial is going to be used since the key that is now requested includes "v2".
I found this method a bit tedious for now since it is easy to forget to update the prefix cache key for the version of the partial. Would be great to have some kind of automated thing for this based on if the partial is changed or something.
But for now, manually updating the prefix seems easiest.
Questions or comments?
I hope this post illustrates how easy it is to use this method on Heroku for some substantial speed improvements. If you need help implement this for your app or on non-Heroku environments please let me know and I'll see if I can help or answer questions!