We recently traced an application bug to an unexpected Rails cache / @memcached@ behavior. At this point, it’s not clear whether this represents a bug in Rails or our misunderstanding of how the Rails cache is supposed to work. Regardless, understanding this behavior may help find (or even avoid) similar bugs.
Consider this code running in a controller in Rails 3.0.5 using @memcached@:
def get_data Rails.cache.fetch("test_data") { [:a, :b, :c] } end def show Rails.cache.delete("test_data") data1 = get_data data1 << :e data2 = get_data Rails.logger.info "Cache Test: #{data1.inspect} -- #{data2.inspect}" render "schedules/show" end
When executing this controller, we see the following in the log:
Cache Test: [:a, :b, :c, :e] -- [:a, :b, :c, :e]
In particular, notice that the second call to @get_data@ returns the _modified_ array.
Now, compare this behavior to this controller test:
describe 'demo cache' do it 'should always get data from the cache' do data1 = controller.get_data data1 << :e data2 = controller.get_data puts "#{data1.inspect} --- #{data2.inspect}" end end
which prints this:
[:a, :b, :c, :e] --- [:a, :b, :c]
In particular, notice that, the second call to @get_data@ returns the original array. Thus, the Rails cache behaves differently in the context of a controller than in the testing context. This difference in behavior made it especially difficult to track down our bug.
h2. A manifestation of a larger bug?
If I try to modify @data2@ in the controller:
def get_data Rails.cache.fetch("test_data") { [:a, :b, :c] } end def show Rails.cache.delete("test_data") data1 = get_data data1 << :e data2 = get_data data2 << :f Rails.logger.info "Cache Test: #{data1.inspect} -- #{data2.inspect}" render "schedules/show" end
Then I get a “can’t modify frozen array” exception on the line that modified @data2@. In other words, the object that is built by @Rails.cache.fetch@ is mutable; but, the object later returned from the cache is frozen. This may be a manifestation of “this bug”:https://rails.lighthouseapp.com/projects/8994/tickets/2655.