memcaches_page plugin for Rails

What is it?

This plugin is very similar to the built-in Rails ‘caches_page’ functionality, except it caches to memcached rather than a file. It relies on the ‘memcached_pass’ nginx directive to serve pages directly from memory if possible, and only passes to rails if necessary. On my server I’ve seen a 75% reduction in Ruby memory usage using this technique.

Disclaimer

This approach is quite heavy-handed, and works best when the content you are serving changes rarely. If you have highly dynamic content, you’re probably better off developing your own, more finely-grained caching. It won’t work if the pages you are serving have some kind of user-specific info in the page
(eg. the logged-in user name in the page header).

Installation

  1. Install a couple of memcache gems
    > sudo gem install memcache-client
    > sudo gem install Ruby-MemCache
  1. Copy memcaches_page.rb into the ‘lib’ directory of your rails app.
  1. Add the following to the bottom of your environment.rb :
    require 'memcaches_page'
    memcache_options = {
    	:c_threshold => 10_000,
    	:compression => true,
    	:debug => false,
    	:namespace => 'code.recurser.com',
    	:readonly => false,
    	:urlencode => false
    }
    MemcachedPageKeyPrefix = '/code'
    MemcachedPageTtl = 604800
    Cache = MemCache.new memcache_options
    Cache.servers = 'localhost:11211'
    A few things to note :

  1. Set the namespace to something appropriate for your app – you’ll need it later when you set up nginx.

  1. The MemcachedPageKeyPrefix should be set if you run your Rails app from a subfolder – leave blank otherwise.

  1. The MemcachedPageTtl is the time-to-live (in seconds) in the cache – I set it for a week which is fairly excessive – you probably only need a few hours, depending on what you’re trying to achieve.

  1. Point Cache.servers at the server/port you are running memcached on.

  1. To cache every action in a controller, add the following filter to any controllers you want to cache :
    after_filter :memcache_page
    Alternatively, add the following line near the top of your controller to cache specific actions only (where ‘view’ and ‘list’ are actions you want to cache :
    memcaches_page :view, :list
    When the page is rendered, the memcaches_page plugin will kick in, and save a copy of the page to your memcache. Rails never uses this cached version directly – in section (7), you’ll configure nginx to check the cache before passing requests to Apache.
  1. Install phusion passenger
    > sudo gem install passenger
    > passenger-install-apache2-module
  1. Configure apache – For example, I run wordpress on my main domain (recurser.com), and a rails app (Redmine) in recurser.com/code/. Apache is configured with the following settings for the recurser.com domain :
    RailsBaseURI /code
    PassengerMaxPoolSize 3
    PassengerMaxInstancesPerApp 2
    PassengerPoolIdleTime 120
    For the complete config, see apache_example.conf
  1. Configure nginx – for the complete config, see nginx_example.conf . The important sections for our purposes are :

  1. Set up a ‘backend’ service called ‘apache’ to pass requests to:
    upstream apache {
    	server 127.0.0.1:8080;
    }
  2. Catch requests to the ‘/code’ subdirectory, and try to serve them from the cache:
    location /code {
    	if ($request_method = POST) {
    		proxy_pass http://apache;
    		break;
    	}
    	default_type  "text/html; charset=utf-8";
    	set $memcached_key  "code.recurser.com:$uri";
    	memcached_pass      127.0.0.1:11211;
    	error_page          404 502 = @backend;
    }
    This does a couple of things :

  • If the request is a POST, serve from Apache and ignore the cache

  • Set the memcached key to lookup, and serve the page directly from memcached if possible (memcached_pass directive).

  • If the page is not in the cache, pass the request back to the ‘backend’. Make sure the code.recurser.com part of the key matches what you set as the e in step (3)!

  1. Set up the ‘backend’ proxy that passes to apache:
    location @backend {
    	proxy_pass         http://apache;
    }



One Response to “memcaches_page plugin for Rails”  

  1. 1 links for 2008-12-03 « Bloggitation


Leave a Reply