Thursday, 3 May 2007

<% cache_unless(params.include?('no_cache_please') , ... do %>

Is adding fragments/actions caching worth it for your Rails application, on your server?
  1. Adding caching to an application complexifies the code (and can be hard to test).
  2. The caching efficiency (in req/sec) depends on many factors : RAM size, CPU power, HD speed, DB caches size, ... A spectacular improvement measured on an old sluggish staging server could be barely noticeable on a modern 4-core machine with 8GB of RAM and the fastest disk money can buy.
Is caching worth it for your application, on your server? It depends. For example, in the Vooruit project the staging server is an old fart with a slow HD and barely enough memory to run 1 Mongrel instance. This is the machine I used to hand-tune the fragment-caching code. I needed to confirm that caching was also improving significantly the performance on the production server. Tip:
  • disable remotely some/all caching on the production server through a parameter in the url:
http://example.com/foobar?no_cache_please
1
2
3
4
5
6
7
8

9
10
11
12
13
14
In the views you want to test :
---------------------------------

<% cache_unless(params.include?('no_cache_please') , :genre => params[:genre]) do%>
   ... the cached code (html and erb)
<% end %>      


In application_helper.rb :
--------------------------

  def cache_unless(condition, name = {}, &block)
    if condition then block.call; return end
    cache(name, &block)
  end

Now you can turn the caching on and off to measure the speed increase on any remote machine:
httperf --server production.example.com --uri /nl --num-conns 100
Reply rate [replies/s]: min 6.6 avg 6.8 max 7.0 stddev 0.3 (2 samples)
Reply rate [replies/s]: min 5.6 avg 6.6 max 7.2 stddev 0.9 (3 samples)


httperf --server production.example.com --uri /nl?no_cache --num-conns 100
Reply rate [replies/s]: min 7.0 avg 7.1 max 7.2 stddev 0.1 (2 samples)
Reply rate [replies/s]: min 3.2 avg 5.0 max 7.2 stddev 1.7 (4 samples)
Was caching worth it? In this case, barely. Updated:
Clarified that this article is mostly about fragments and actions caching.

2 comments:

Jan said...

Caching has proven much more effective to me when making my rails app cache to HTML pages which get server by my lighttpd, so it doesn't have to pass any Rails. In that case it does go faster

Alain Ravet said...

@Jan

(I updated the text to clarify the article is about fragments and actions caching)

True, page caching (to HTML static files) is as powerful as it's easy to add in Rails.
All the Vooruit feeds are actually static files generated that way :

class FeedsController < ApplicationController
    # pages are expired by sweepers
    caches_page :concerts, :headlines
    session :off

    def concerts; ... end
    def headlines; ... end
end

Limitations: if your page contents can vary :
  1/ from visitor to visitor (anonymous or logged in), or
  2/ from request to request (randomly changing images set),
caching to static html can prove hard to implement (and test).


You can solve (1/) with Apache RewriteRule. See:
condition use of page cache in rails

(2/) is harder, and requires moving the server action to javascript code embedded in the page. I'll write about this solution later.