Last summer I wrote the activerecord-delay_touching gem to fix a performance problem. The gem has been used in production for six months on multiple sites. I am writing now to propose that I merge this gem into Rails.
I’d like to get feedback on this proposal before I create the PR, because it’s a non-trivial amount of work. (E.g., replace Rspec with Minitest, remove Timecop from the specs, etc.)
The problem: for key-based expiration of fragment caches in Rails, you typically use
touch: true on the
belongs_to statement. But when you create or update N records that all belong_to the same owning record, the cascading touches cause that record to be touched N times. All those DB round-trips turn out to be really slow. It’s similar to the traditional N+1 query problem, only for updates. This can happen even if you only make a single
save call–e.g., when you use accepts_nested_attributes_for.
The solution: batch up your ActiveRecord “touch” operations into the smallest possible number of DB round-trips. This speeds things up a LOT. (I had numbers last summer from the app I was working on; I’ve since lost them. I’ll work on regenerating them.)
How: by calling
delay_touching. I modeled this API on the
no_touching API that was introduced in Rails 4.1:
ActiveRecord::Base.delay_touching do @person.update(person_params) end
There’s more detail about what the gem does, how it works, and a couple of potential gotchas in the README:
Feedback? Shall I go ahead?