DEV: Update DB.after_commit to be compatible with 'real' transactions (#11294)

Previously it matched the behavior of standard ActiveRecord after_commit callbacks. They do not work well within `joinable: false` nested transactions. Now `DB.after_commit` callbacks will only be run when the outermost transaction has been committed.

Tests always run inside transactions, so this also introduces some logic to run callbacks once the test-wrapping transaction is reached.
This commit is contained in:
David Taylor
2020-12-08 00:03:31 +00:00
committed by GitHub
parent 76b04afca3
commit ed91385c18
3 changed files with 51 additions and 21 deletions

View File

@ -32,7 +32,12 @@ class MiniSqlMultisiteConnection < MiniSql::Postgres::Connection
end
def committed!(*)
@callback.call
if DB.transaction_open?
# Nested transaction. Pass the callback to the parent
ActiveRecord::Base.connection.add_transaction_record(self)
else
@callback.call
end
end
def before_committed!(*); end
@ -42,16 +47,25 @@ class MiniSqlMultisiteConnection < MiniSql::Postgres::Connection
end
end
def transaction_open?
ActiveRecord::Base.connection.transaction_open?
end
if Rails.env.test?
def test_transaction=(transaction)
@test_transaction = transaction
end
def transaction_open?
ActiveRecord::Base.connection.current_transaction != @test_transaction
end
end
# Allows running arbitrary code after the current transaction has been committed.
# Works with nested ActiveRecord transaction blocks. Useful for scheduling sidekiq jobs.
# If not currently in a transaction, will execute immediately
def after_commit(&blk)
return blk.call if !ActiveRecord::Base.connection.transaction_open?
# In tests, everything is run inside a transaction.
# To run immediately, check for joinable? transaction
# This mimics core rails behavior: https://github.com/rails/rails/blob/348e142b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb#L211
return blk.call if Rails.env.test? && !ActiveRecord::Base.connection.current_transaction.joinable?
return blk.call if !transaction_open?
ActiveRecord::Base.connection.add_transaction_record(
AfterCommitWrapper.new(&blk)