Caching with Ruby on Rails

It just needed to happen. The Hardcore Racing eStore seemed slower every day. It started off as just an attempt to cache the make/model dropdown since we now wanted to put it in the search box in the left sidebar, which is visible on every page. It was simply an unrealistic number of queries to build that dropdown—with all the other queries I expected to make for a page’s request.

Read on to learn how I made my site FAST!

With Rails, there are three types of caching. First, there’s page caching. This is the fastest method because the entire generated page from the first request gets stored on the disk inside the document root. So next time the web server receives that request, the file is served right up off the disk without even invoking the rails application. Here’s an example of an imaginary blog application caching a post page:

1
2
3
class BlogController < ApplicationController
  caches_page :post
end

Just one line, and each post page will only be generated once. This is very fast, but if you want to put a “Welcome username” message in the corner of your page, forget it: you can no longer serve up the exact same page to everyone.

The second two methods both use a different way of remembering the html snippit from the first. By default, rails is configured to store these bits right in the running application’s memory.

If page caching is the most general type cache, the next in line is action caching. Action caching is achieved the same way as page caching except you use caches_action instead. In this case, the action’s result will be cached as a fragment. By doing this, the ActionPack is still dispatched, so you can still authenticate before serving up the page. It also affords you the ability to keep dynamic content in the sidebar if need be.

The final type of caching is the most flexible: fragment caching. Watch how easy this is:


blah blah some code to build my complicated dropdown...

Simply by wrapping my output in this way, I’ve told rails to remember what’s generated from inside the code block. By default, this stored in memory, but you can change it if you read the documentation.

The only trick in this last case is to avoid actually executing those costly queries to the database from the controller that you’ve so toiled to prevent! Imagine we were fragment caching our blog post. We only want to query the database if there’s no fragment returned by read_fragment:

1
2
3
4
5
6
7
class BlogController < ActionController
  def post
    unless read_fragment :action => 'post'
      @post = Post.find(params[:id])
    end
  end
end

The Other Half of the Equation

So this is all well and good, but what if some of this data changes? We need a way to expire the cached data - remove it from memory, delete the file on the disk - when its data has or potentially has changed. Thankfully, Rails again makes this very easy for you.

Let’s say someone edits the post in our imaginary blog. In our action that saves the changes to the database, we’d just sneak in this line:


expire_page :controller => 'blog', :action => 'post', :id => post

Of course, apart from a hash like this, you can also pass expire_cache a string representing the url or even a regular expression to expire pages en masse.

…And people think my job is hard.

Thursday, December 15th, 2005 Software

3 Comments to Caching with Ruby on Rails

  1. I will give it a try. Thanks

  2. hmmm on December 15th, 2005
  3. I think your "expire_cache" (in "The Other Half of the Equation" section) should be "expire_page", since as far as I can tell, expire_cache doesn’t exist except for on this site.

    Cheers for the nice tutorial though!

  4. Dave Silvester on December 15th, 2005
  5. Thanks for catching that, Dave. I’ve made the correction in the article.

  6. thread on December 15th, 2005

@djthread

My Flickr Photostream

A photo on Flickr
A photo on Flickr
A photo on Flickr
A photo on Flickr
A photo on Flickr
A photo on Flickr
 

Categories

My History