ActiveRecord object caching in Ruby on Rails

Yesterday I started playing with basic caching for PriceChirp and tried what I thought would be easy. Boy was I wrong. It turns out what I was attempting to do is not supported by :memory_store in the development environment. Before moving to :mem_cache_store, I was able to find a work around. The work around is outlined below for those who do not have the option of using memcached. However, if you can use memcached, it is by far the better route to take.

My goal was to cut down on the number of database hit by caching the resultant ActiveRecord object in :memory_store.


#models/foo.rb
 def self.cool_widget
 Rails.cache.fetch('cache_object_name') { find_by_name('cool_widget') }
 end

#config/environments/development
 config.action_controller.perform_caching = true
 config.cache_store = :memory_store

#controler/foos_controllers.rb
 awesome_widget = foo.cool_widget
 logger.info awesome_widget.id

In script/console and in rails production, this works as you would expect. However, in the rails development environment, funny things happen. On the first page view, all looks good. On the second page view, as rails attempts to pull the object out of cache, rails cashes with the following error:

 .../activerecord/lib/active_record/attribute_methods.rb:142:in `create_time_zone_conversion_attribute?'
 

A google search lead me to:

https://rails.lighthouseapp.com/projects/8994/tickets/1290-activerecord-raises-randomly-apparently-a-timezone-issue

Here they talk known problems about caching Active Record objects on development servers. Something about the way the object are torn down between page loads to allow for every hit to get a new instance of the controller.

At the very end of the discussion (over the course of a year), I got the following work around:

http://gist.github.com/251886


# put this in lib/nil_store.rb:
 class NilStore < ActiveSupport::Cache::Store
 def initialize(location='//myloc'); end
 def read(name, options = nil); super; end
 def write(name, value, options = nil); super; end
 def delete(name, options = nil); super; end
 def delete_matched(matcher, options = nil); super; end
 end

# and then in environments/development.rb:

require 'nil_store'
 config.cache_store = NilStore.new

# or you could just turn on development class caching:

config.cache_classes = true

This “nil_store” routine basically prevent caching in the development environment which makes everything work. On production, I did not notice any problems.

Other work arounds to this bug include, not caching the ActiveRecord object or using a different caching routine, like memcached.