Caching

Notes on how we do caching.

Types of Rails caching
Rails has various kinds of caching, at an object, page, action, or fragment level. Right now (early June 2013, when we first started caching) we are mostly doing fragment caching, for certain partials etc. that New Relic tells us are particularly slow.

Config/setup/etc
Install the plugin on heroku:

heroku addons:add memcachier:add

In the Gemfile:

gem 'dalli' # memcache client gem 'memcachier' # heroku tool to connect dalli to their addon

In config/environments/production.rb and staging.rb:

config.consider_all_requests_local = true config.cache_store = :dalli_store

In dev, it's usually false/:memory_store; if you want to test and play with it, set it to true/:memory_store.

Caching a fragment
All you have to do is wrap a chunk of HAML in a call to "cache". Give the fragment a name so we can refer to it later, when we want to invalidate it.

- cache('fragment_name') do     %h2 This is a bit of HAML - current_member.plantings.each do |p| ... etc ...

Expiring fragments from the cache
Quick version:

expire_fragment('fragment_name')

The real thing, though, is where do you call this from? Well, there's a thing called a Sweeper, which you can stick in app/models alongside your model class, and which keeps an eye out for changes and has callbacks for when they occur.

This is our app/models/post_sweeper.rb

class PostSweeper < ActionController::Caching::Sweeper observe Post def after_create(post) expire_fragment('recent_posts') end def after_update(post) expire_fragment('recent_posts') end def after_destroy(post) expire_fragment('recent_posts') end end

(Why in app/models? Because that's what a bit of googling/various stack overflow answers suggested was a common place to put them.)

You also have to make sure the sweeper is called, by putting this in the posts_controller.rb:

cache_sweeper :post_sweeper

Testing
Here's what I did on my local dev machine. I did all the above, set the cache store to "memory_store", then ran up "rails s" locally.

I navigated around the site, making new posts and plantings, editing them, and deleting them. I saw that they were updated in the partials on the homepage. If I removed teh "cache_sweeper" line from the controller, they were *not* updated. This showed me that the cache was functioning.

Deployment
As mentioned above, you have to add the memcachier heroku addon. Wait a few minutes and run "heroku config" to ensure that the MEMCACHIER_* credentials are set. You can then do something like:

heroku run console --app yourapp >> require 'dalli' >> require 'memcachier' >> dc = Dalli::Client.new >> dc.set('foo', 'bar') => true >> dc.get('foo') => "bar" >> dc.stats => {"memcachier.example.net:11211" => {"cur_items" => "49982", "bytes" => "89982234"} }

This will show you that it's all working.

Deploy to heroku as usual (git push...) and things Should Just Work[tm].

Memcachier:dev has some very simple analytics which are accessible via the Heroku website, by clicking on the name of the relevant addon under the relevant app. When everything's running, you should see a non-zero amount of memory used there. For instance, on our staging site with just two small fragments cached, it says:

Cache Limit 	25 MBs Current Usage 	0.002 MBs

More docs

 * Heroku memcachier docs
 * Rails Guides: Caching