current_transaction.after_commit is handy

Posted . Visible to the public.

Rails 8 provides a new way of doing things after the current transaction, without defining a model "global" after_commit callback.

def do_something
  update_column(:foo, "bar")
  send_email(foo: "bar")
end

The above code is brittle if it's called within a failing transaction:

ActiveRecord::Base.transaction do
  user.do_something
  raise "oh no, my transaction failed"
end

In the case above, the database update is lost, but the email is sent out. Also, the database transaction is long (this is bad for performance in general) because it waits for send_email, which depending on its implementation makes slow network calls.

You can now rewrite this with current_transaction to without having to define a global after commit callback:

def do_something
  update_column(:foo, "bar")
  ActiveRecord::Base.current_transaction.after_commit { send_email(foo: "bar") }
  ActiveRecord::Base.current_transaction.after_rollback { Rails.logger.warn("rollback") } # after_rollback is also supported
end
Profile picture of Niklas Hä.
Niklas Hä.
Last edit
Felix Eschey
License
Source code in this card is licensed under the MIT License.
Posted by Niklas Hä. to makandra dev (2026-07-07 11:44)