Rails 3 best practices: caching

This article is excerpted from the book The Rails 3 Way, published by Pearson/Addison-Wesley Professional as part of its Professional Ruby series. Reprinted with permission of Pearson Education Inc. all rights reserved.

Hard work never killed anybody, but why take the chance?
--Edgar Bergen (as Charlie McCarthy), 1903-1978

Historically Rails has suffered from an unfair barrage of criticisms over perceived weaknesses in scalability. Luckily, the continued success of Rails in ultra-high-traffic usage at companies such as Twitter and Groupon has made liars of the critics.

Nowadays, you can make your Rails application very responsive and scalable with ease. The mechanisms used to squeeze maximum performance out of your Rails apps are the subject of this article.

View caching lets you specify that anything from entire pages down to fragments of the page should be captured to disk as HTML files and sent along by your web server on future requests with minimal involvement from Rails itself. ETag support means that in best-case scenarios, it's not even necessary to send any content at all back to the browser, beyond a couple of HTTP headers. Hard work may never have killed anyone, but make your Rails application work harder than it needs to, and you might kill your server!

View Caching

There are three types of view caching in Rails:

Page caching. The output of an entire controller action is cached to disk, with no further involvement by the Rails dispatcher. Action caching. The output of an entire controller action is cached to disk, but the Rails dispatcher is still involved in subsequent requests, and controller filters are executed. Fragment caching. Arbitrary bits and pieces of your page's output can be cached to disk to save the time of having to render them in the future.

Caching in Development Mode? I wanted to mention up front that caching is disabled in development mode. If you want to play with caching during development, you'll need to edit the following setting in the config/environments/development.rb file:

config.action_controller.perform_caching = false

Of course, remember to change it back before checking it back into your project repository, or you might face some very confusing errors down the road.

(In his great screencast on the subject, Geoffrey Grosenbach suggests adding another environment mode to your project named developmentwithcaching, with caching turned on just for experimentation.)

Page Caching

The simplest form of caching is page caching, triggered by use of the caches_page macro-style method in a controller. It tells Rails to capture the entire output of the request to disk so that it is served up directly by the web server on subsequent requests without the involvement of the dispatcher. Nothing will be logged to the Rails log, nor will controller filters be triggered -- absolutely nothing to do with Rails will happen, just like the static HTML files in your project's public directory.

Action Caching

By definition, if there's anything that has to change on every request or specific to an end user's view of that page, page caching is not an option. On the other hand, if all we need to do is run some filters that check conditions before displaying the page requested, the caches_action method will work. It's almost like page caching, except that controller filters are executed prior to serving the cached HTML file. That gives you the option to do some extra processing or even redirect if necessary.

Action caching is implemented with fragment caching and an around_filter. The cached action content is keyed based on the current host and the path, which means that it will still work even with Rails applications serving multiple subdomains using a DNS wildcard. Also, different representations of the same resource, such as HTML and XML, are treated like separate requests and cached separately.

The list below is taken from a blog application with public and private entries, so for default requests, we should run a filter that figures out whether the visitor is logged in and redirects them to the publication if necessary.

Listing 1: The EntriesController of liljournal

class EntriesController < ApplicationController<br>   before_filter :check_logged_in, :only => [:index]<br> <br>   caches_page :public<br>   caches_action :index<br> <br>   def public<br>     @entries = Entry.where(:private => false).limit(10)<br>     render :index<br>   end<br> <br>   def index<br>     @entries = Entry.limit(10)<br>   end<br> <br>   private<br> <br>     def check_logged_in<br>       redirect_to :action => 'public' unless logged_in?<br>     end<br> <br> end<br>
1 2 3 4 5 Page 1
Page 1 of 5
Shop Tech Products at Amazon