Using multiple updated_at timestamps for caching in Rails
It sounds so obvious when spelled out but you can use multiple timestamps in Rails models that each individually can serve as cache keys for different views. For example say you have an Article that looks like this:
You’ll most likely have a partial for the article:
and you’re incrementing the
number_of_views in a background job with some kind of rate limiting or debouncing doing something like:
So, pointing out the obvious is that that’s invalidating the cached partial above even though the
number_of_views data is not used in the partial itself. This is happening because if
.save persists changes to a model it sets the
updated_at property of that model to the current datetime – as you would expect. Furthermore, by defauly rails uses
updated_at as part of it’s cache key.
I’ve mitigated this in two ways. First, for models that are ‘small’ you can use
update_columns like this:
update_columns does not trigger callbacks, validations or increments to updated_at.
Second, in cases where there are two separate fragments that need to be cached. For instance, if you had the user partial and the admin partial. The admin is not really concerned with the content but just about the latest stats:
In this scenario you have to maintain the two separate timestamps but that’s easy enough:
You can then get super fancy by creating a hash of which properties trigger changes to which timestamps and then using
self.changes to get a full list of attributes changed. Based on that list you can look up which timestamps to update. Finally, roll all of that into a nice concern and you can have it everywhere with minimal effort.
The scenario above, of course, is an over simplification but it does illustrate a point. Imagine if the article contains embedded generated graphics, or content from other data structures. If you start getting a large number of hits your cache will constantly be invalidated and your servers will constantly have to regenerate the graphic and hit the database for the other data.
Get in touch
Or send me feedback. It will take less than 20 seconds.