Keeping Data in Sync with ActiveRecord::UpdatedAt
ActiveRecord does not touch updated_at
by default when calling:
This was causing some unexpected issues with caching and incremental ETL to external systems since our usage of updated_at
to signify data modifications was inconsistent.
We realized that we rarely ever have a reason to modify data WITHOUT touching updated_at
so we’ve enabled the touching behavior by default using the active_record-updated_at gem.
This gem simply patches ActiveRecord::Relation#update_all
to automatically specify updated_at
as Time.current
when:
- An
updated_at
column exists on the table - An
updated_at
value is not explicitly specified in the query
# This touches `updated_at` with `Time.current`
User.update_all(role: “member”)# This sets `updated_at` to `1.day.ago`
User.update_all(role: “member”, updated_at: 1.day.ago)# This sets `updated_at` to `NULL`
User.update_all(role: “member”, updated_at: nil)
The update_column
and update_columns
methods actually call update_all
under the hood so these methods are patched as well.
# This touches `updated_at` with `Time.current`
user.update_column(:role, "member")# This sets `updated_at` to `1.day ago`
user.update_columns(role: "member", updated_at: 1.day.ago)
For those rare occasions that we don’t want updated_at
to be automatically touched we can explicitly wrap these method calls in a disable block:
ActiveRecord::UpdatedAt.disable do
User.update_all(role: “member”)
User.find(123).update_column(name: "Sean")
end
We’ve found this to be a simpler solution than manually updating all call sites to these methods with explicit updated_at
timestamps and requiring all of our engineers to remember this step going forward.
Our engineering team is still growing! We’re hiring engineers in our San Francisco and Columbus, OH offices. Check out our careers page to learn more. We look forward to hearing from you!