Upsert_all uses DB time for timestamps, which is inconsistent to regular timestamp updates

I’m using rails 7.1 and postgresql 13 , and try to adopt upsert_all to my needs, and I noticed it uses CURRENT_TIMESTAMP PG function to set a value for created_at/updated_at timestamps.

I believe it’s inconsistent to regular create/update behaviour, because it relies on database time rather than application time. I see at least 2 reasons why we shouldn’t do that:

  • CURRENT_TIMESTAMP uses DB transaction start time, which is incorrect if upsert happens in the middle of long-running transaction. I believe timestamp should display exact time of call, e.g. to find it in app logs.
  • it’s impossible to test timestamps with time travelling approach, timestamps are always computer time, not stubbed time.

I did this quick&dirty monkeypatch in my app, and it seems to work:

module ActiveRecord
  class InsertAll
    attr_reader :current_timestamp

    alias_method :old_initialize, :initialize

    def initialize(model, inserts, on_duplicate:, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
      @current_timestamp = Time.current

      old_initialize(model, inserts, on_duplicate:, update_only:, returning:, unique_by:, record_timestamps:)


    # Overridden connection.high_precision_current_timestamp by @current_timestamp
    def timestamps_for_create

    class Builder
      # Overridden connection.high_precision_current_timestamp by @current_timestamp
      def touch_model_timestamps_unless(&block)
        return "" unless update_duplicates? && record_timestamps?

        model.timestamp_attributes_for_update_in_model.filter_map do |column_name|
          if touch_timestamp_attribute?(column_name)
            # only this SQL is overridden ----------------------------------------------------------------------------------------------------vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
            "#{column_name}=(CASE WHEN (#{" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE #{connection.quote(insert_all.current_timestamp)} END),"


User.upsert_all([{ email: '', country_id: 1 }], unique_by: %i[email])

Generated SQL query originally

INSERT INTO "users" ("email","country_id","created_at","updated_at") VALUES ('', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) ON CONFLICT ("email") DO UPDATE SET updated_at=(CASE WHEN ("users"."country_id" IS NOT DISTINCT FROM excluded."country_id") THEN "users".updated_at ELSE CURRENT_TIMESTAMP END),"country_id"=excluded."country_id" RETURNING "id"

and with monkey patch:

INSERT INTO "users" ("email","country_id","created_at","updated_at") VALUES ('', 1, '2024-06-22 22:29:40.917937', '2024-06-22 22:29:40.917937') ON CONFLICT ("email") DO UPDATE SET updated_at=(CASE WHEN ("users"."country_id" IS NOT DISTINCT FROM excluded."country_id") THEN "users".updated_at ELSE '2024-06-22 22:29:40.917937' END),"country_id"=excluded."country_id" RETURNING "id"

Which is also works correctly.

Was there a reason why it was chosen to use DB functions instead of app time for timestamp? Is there a reason my monkey patch is incorrect?