Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Unexpected after_commit and after_transaction behaviour with ActiveRecord Persistence #851

@ggambetti

Description

@ggambetti

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:

  1. Checkout my fork: git checkout [email protected]:ggambetti/aasm.git
  2. Switch to the issue branch: git checkout ggambetti/after-transaction-behaviour (9b56095ab74e794eed7e56d6aca7a125253d5c84 at time of writing)
  3. Using Ruby 3.1.5
  4. bundle install --gemfile gemfiles/rails_7.1.gemfile
  5. bundle exec rspec spec/unit/persistence/active_record_persistence_spec.rb
  6. Note that the newly added tests fail, showing inconsistent behaviour during nested transactions between after_commit and after_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
      end
Failures:

  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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions