We upgraded a Rails 3 application to use Rails 4, and found that certain pages weren’t being updated as we expected. The problem was that cached fragments were no longer being expired properly.
Briefly: The application was explicitly expiring cached fragments by name. After the upgrade to Rails 4, this code didn’t have the intended effect because the templates were creating different fragment names; the application was no longer expiring the fragment names that the templates were actually using.
Let’s look at this in more detail. In Rails 3, you could have a template with this code:
<% cache 'hello' do %> ... <% end %>
This would cache the fragment in the block under a name derived from your argument. In this example, we supplied the argument “hello” and the name generated would be “views/hello”.
Elsewhere in your application, you could explicitly expire that fragment, so that the next time the fragment was referenced, it would be regenerated. You’d do that by sending this message to a controller object:
This would generate the full name, “views/hello”, and expire the cached fragment with that name.
Say we start using Rails 4. Now the caching code in the template appends a new piece of information – a cache digest – to the fragment name it generates. The cache digest will depend on the current template and its dependencies. In our example, the fragment name might be something like “views/hello/e3225f7c7bbadd41c6ea556b7b7a03b1”.
The code that expires the fragment, however, is outside the context of this template, so it does not know the cache digest. As with Rails 3, it will try to expire the fragment named “views/hello” – but that’s not what our fragment is called anymore. The fragment, then, is no longer being expired.
How do we fix this?
Well, ideally, the application wouldn’t be expiring fragments explicitly. Instead of a string literal, the template would pass a relevant object as the argument to
cache, and that object would generate its own cache key. The code for generating the key would be carefully designed so that the key would only change if the object changed in such a way that it might need to be presented differently.
But you go to war with the code you have, not the code you wish you could have. If the application will continue to expire fragments explicitly, then one simple solution is to stop appending the cache digest. We found every fragment we were trying to explicitly expire and passed the option
:skip_digest => true to
cache. So the template code looked like this:
<% cache 'hello', :skip_digest => true do %> ... <% end %>
This solved our problem – well enough for the time being, at least.
By the way: If you ran into this problem during a Rails upgrade, the addition of cache digests created some inconvenience for you. Don’t get annoyed, though. This is, in fact, a very cool feature of Rails 4: cache digests allow for Russian doll caching.
No app left behind: Upgrade your application to Ruby 3.0 and s...
A look forward from 2020
Testing Rails applications on real mobile devices (both design...
reinteractive is Australia’s largest dedicated Ruby on Rails development company. We don’t cut corners and we know what we are doing.
We are an organisation made up of amazing individuals and we take pride in our team. We are 100% remote work enabling us to choose the best talent no matter which part of the country they live in. reinteractive is dedicated to making it a great place for any developer to work.
Webinars are our online portal for tips, tricks and lessons learned in everything we do. Make the most of this free resource to help you become a better developer.
The Ruby on Rails Installfest includes a full setup of your development environment and step-by-step instructions on how to build your first app hosted on Heroku. Over 1,800 attendees to date and counting.
The Ruby on Rails Development Hub is a monthly event where you will get the chance to spend time with our team and others in the community to improve and hone your Ruby on Rails skills.