How to bust your Rails etag cache on deployment.
UPDATE: I created a gem to fix the problem I start to discuss below.
You should read the updated post about this topic, here. I describe the problem of Rails caching of etags and the gem in more detail.
Original Post #
If you’re writing a Rails app that’s getting any traffic whatsoever, you probably are using some method of caching.
Client side caching with etags has been a wonderful addition to Ruby on Rails. That, combined with lazy loading of your queries, and you can save a ton of processing without needing to worry about page or action caching.
Here’s such a simple tip that wasn’t at all intuitive to me at first.
How do you bust your client side caches when you deploy your application?
If you look in the official Rails docs you can find out a tiny bit more about the methods: fresh_when and stale?, but they don’t teach anything on this issue of deployment.
In other words, if clients are caching all sorts of rendered HTML on their side, and I deploy new code that changes those templates, how do I make sure the clients fetch the new code?
If you go down the rabbit hole just a bit, you’ll find that fresh_when makes a call to etag. And etag makes a call to ActiveSupport::Cache.expand_cache_key. And therein the source to expand_cache_key lies the answer.
# File activesupport/lib/active_support/cache.rb, line 77
def expand_cache_key(key, namespace = nil)
expanded_cache_key = namespace ? "#{namespace}/" : ""
if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
expanded_cache_key << "#{prefix}/"
end
expanded_cache_key << retrieve_cache_key(key)
expanded_cache_key
end
You need to make sure ENV[“RAILS_CACHE_ID”] or ENV[“RAILS_APP_VERSION”] changes on deployment.
So I created an initializer called bust_cache.rb in my Rails.root/config/initializers. And all it does is initialize a new RAILS_CACHE_ID:
ENV["RAILS_CACHE_ID"] = Time.now.to_s
That’s it. Now, after every deploy to Heroku, my RAILS_CACHE_ID is regenerated, causing all my previously generated etags to be stale, forcing all the clients hitting my web app to refetch their pages.
P.S. I’ve been working on a secret project that attempts to turn ways in which we procrastinate into a strength. If you want to give it a try soon, you should follow me on Twitter: here.