-
Notifications
You must be signed in to change notification settings - Fork 645
Description
Describe the bug
The after_commit and after_transaction event callbacks don't behave the same way around nested transactions with ActiveRecord persistence. after_commit runs whenever the top level transaction commits, while after_transaction runs during the AASM event - ignoring nested transactions.
after_commit nested transaction behaviour was noted ~6 years ago in 2018 in #536, and it appears that after_transaction was not noticed then. The linked issue explains why after_commit needed changing then, which is equally true for after_transaction now.
To Reproduce
Steps to reproduce the behaviour:
- Checkout my fork:
git checkout [email protected]:ggambetti/aasm.git - Switch to the issue branch:
git checkout ggambetti/after-transaction-behaviour(9b56095ab74e794eed7e56d6aca7a125253d5c84at time of writing) - Using Ruby
3.1.5 bundle install --gemfile gemfiles/rails_7.1.gemfilebundle exec rspec spec/unit/persistence/active_record_persistence_spec.rb- Note that the newly added tests fail, showing inconsistent behaviour during nested transactions between
after_commitandafter_transaction.
Expected behavior
Tests pass, showing that after_transaction runs around the ActiveRecord transaction lifecycle, and not during a nested transaction.
Test Output
Delta between master and ggambetti/after-transaction-behaviour.
describe "after_transaction callback" do
context "nested transaction" do
it "should fire :after_transaction after the root transaction" do
validator = Validator.create(:name => 'name')
expect(validator).to be_sleeping
validator.transaction do
validator.run!
expect(validator.after_transaction_performed_on_run).to be(false) # fails
end
expect(validator.after_transaction_performed_on_run).to be(true)
end
it "should fire :after_transaction if root transaction failed" do
validator = Validator.create(:name => 'name')
expect(validator).to be_sleeping
validator.transaction do
validator.run!
expect(validator.after_transaction_performed_on_run).to be(false) # fails
raise ActiveRecord::Rollback, "failed on purpose"
end
expect(validator.after_transaction_performed_on_run).to be(true)
end
end
endFailures:
1) transitions with persistence transactions after_transaction callback nested transaction should fire :after_transaction after the root transaction
Failure/Error: expect(validator.after_transaction_performed_on_run).to be(false) # fails
expected false
got true
# ./spec/unit/persistence/active_record_persistence_spec.rb:662:in `block (6 levels) in <top (required)>'
# ./spec/unit/persistence/active_record_persistence_spec.rb:660:in `block (5 levels) in <top (required)>'
2) transitions with persistence transactions after_transaction callback nested transaction should fire :after_transaction if root transaction failed
Failure/Error: expect(validator.after_transaction_performed_on_run).to be(false) # fails
expected false
got true
# ./spec/unit/persistence/active_record_persistence_spec.rb:674:in `block (6 levels) in <top (required)>'
# ./spec/unit/persistence/active_record_persistence_spec.rb:672:in `block (5 levels) in <top (required)>'
Finished in 0.26892 seconds (files took 0.70088 seconds to load)
80 examples, 2 failures
Failed examples:
rspec ./spec/unit/persistence/active_record_persistence_spec.rb:656 # transitions with persistence transactions after_transaction callback nested transaction should fire :after_transaction after the root transaction
rspec ./spec/unit/persistence/active_record_persistence_spec.rb:668 # transitions with persistence transactions after_transaction callback nested transaction should fire :after_transaction if root transaction failed