Rails fragment caching (and Rails caching in general)

Well I finally had some time to spare in order to implement a decent and easy to use caching system into my first, and therefore most loved, Rails project.

There is a plenty of information on the net regarding Rails caching, but somehow, too much info turned out into having no info I could readily use at all :) I was even writing about caching in Rails myself in this post: "Simple and fast caching in Rails". But I definitely can't say I'm a Rails caching expert in any way though.

For those in the same boat as myself, here's a simple intro into Rails caching (mostly in form of external links), and possible approaches.

First of all, there's a very nice slideshow regarding different types of caching in Ruby on Rails: Content Caching in Rails. You might want to check it out before proceeding.

There's also a nice write-up by Robert Evans: "Rails Caching", which tought me some useful things in the very beginning of my research.

So, lets go.

Basically, there are 3 different types of caching in Rails: page caching, action caching and fragment caching.

1. Page caching

Page caching is the easiest to implement but most difficult to control in any useful way. Though the presentation suggests you to keep away from page caching, there's at least one case you might love to use it. And that's caching of server-side processed images. Like.. you have an image controller, which takes a picture from database, processes it (resizing, etc) and returns the result to user. Each this action only needs to be performed once and then the results can safely be cached, because it's very unlikely an image in database will change. And if it will change, it will most likely be re-uploaded with a different ID, so a new cache file will be created.

Have a look at a real-life example (I use the excellent FlexImage plugin for most of my image processing tasks)

class ImageController < ApplicationController
flex_image :action => 'medium', :class => Image, :size => '275×275'
caches_page :medium

…..

end

Resizing of the image of a given ID is only performed once, so can be safely page-cached, resulting in lightning-fast performance.

2. Actions caching (and model caching plugin)

I have wrote about actions caching before, and you can read my little blog entry on this matter. What worths noting is that action caching, unlike page caching, allows you to perform filtering in controllers, so you can use this type of caching on pages which require authentication, or change in any way depending on who watches them.

There's also a nice plugin for caching on model level exists. It's called "Caches.rb" and available at Yurii Rashkovskii site. What is does it allowing you to cache results of functions of models. For example, lets say you have a model called Posts, and you have a very calculation-intensive function which creates a list of popular posts with their popularity ranking and stuff like that. By using the plugin, you can cache the result of the calculation and use it without recalculation until the results auto-expires (and you can set the auto-expiration time limit for any action).

Now, honestly, I have tried to use Yurii's plugin, but something didn't turned out good for me and I was getting some weird errors. And I was in a hurry and stuff so I just made a bookmark to the site and moved on to a different solution (model caching was not the best solution for me, anyway). But the plugin clearly works for alot of people so you might want to try it out.

3. Fragment caching

This technique is the most flexible caching solution, but is hardest to manage… Unless.. unless you know about this fantastic plugin: timed_fragment_cache plugin for Rails.

Here's what you need to do in order to be able to use it:

Install the plugin:

script/plugin install -x http://svn.livsey.org/plugins/timed_fragment_cache

You have to specify caching store in your environment.rb file:

ActionController::Base.fragment_cache_store = :file_store, "#{RAILS_ROOT}/tmp/cache"

Of course you can use any path or storage solution you like (there are other solutions than file_store, but you can read about them elsewhere. For me, and I think for most other users and uses, :file_store is the best option.

And if you wonder where the cached files went, have a look here:

fragment caching in rails

There's one catch regarding testing caching in Rails, overall. The thing is that caching is not enabled by default when you use development environment. So if you think cache should work, but it doesn't for you, you might want to check if you are running in development environment. If you are, you might need either temporarily relaunch you app in production environment ( in case of Mongrel server the command is: mongrel_rails start -e production ) or modify your environment files so that caching is ON in development environment.

As I have told before, I am in no way consider myself a Rails caching expert, so forgive me if there are some mistakes or omissions, but I hope that this entry will be of help to those who just start implementing caching on their - currently - slow loading sites :) Comments and thoughs are welcome, as always.

Leave a Reply