Keeping Data in Sync with ActiveRecord::UpdatedAt

Sean Huber
Kiavi Tech
Published in
2 min readDec 13, 2016
Photo by Kayla Kandzorra

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!

--

--