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.