Skip to content

The outermost after_commit callback not executed upon exception within nested transaction #854

@kevink1103

Description

@kevink1103

Describe the bug
When there are multiple after_commit callbacks within nested transaction and an exception occurs, the last after_commit callback is not fired while others are.

To Reproduce

class Test < ApplicationRecord
  include AASM

  aasm column: :state, timestamp: false do
    state :initial, initial: true
    state :submitted, :accepted, :ready, :completed

    event :submit, after: :after_submit, after_commit: :after_commit_submit do
      transitions from: :initial, to: :submitted
    end

    event :accept, after: :after_accept, after_commit: :after_commit_accept do
      transitions from: :submitted, to: :accepted
    end

    event :ready, after: :after_ready, after_commit: :after_commit_ready do
      transitions from: :accepted, to: :ready
    end

    event :complete, after: :after_complete, after_commit: :after_commit_complete do
      transitions from: :ready, to: :completed
    end
  end

  def after_submit
    ap "AFTER_SUBMIT"
    accept!
  end
  def after_commit_submit
    ap "AFTER_COMMIT_SUBMIT"
  end

  def after_accept
    ap "AFTER_ACCEPT"
    ready!
  end
  def after_commit_accept
    ap "AFTER_COMMIT_ACCEPT"
  end

  def after_ready
    ap "AFTER_READY"
    complete!
  end
  def after_commit_ready
    ap "AFTER_COMMIT_READY"
  end

  def after_complete
    ap "AFTER_COMPLETE"
  end
  def after_commit_complete
    ap "AFTER_COMMIT_COMPLETE"
    raise "Oh my god, something's wrong!"
  end
end

when running Test.create!.submit!, after_commit_submit won't get called.

"AFTER_SUBMIT"
"AFTER_ACCEPT"
"AFTER_READY"
"AFTER_COMPLETE" --> innermost transaction is commited
"AFTER_COMMIT_COMPLETE"
"AFTER_COMMIT_READY"
"AFTER_COMMIT_ACCEPT"

# where is "AFTER_COMMIT_SUBMIT"? --> outermost callback not executed

Expected behavior

"AFTER_SUBMIT"
"AFTER_ACCEPT"
"AFTER_READY"
"AFTER_COMPLETE"
"AFTER_COMMIT_COMPLETE"
"AFTER_COMMIT_READY"
"AFTER_COMMIT_ACCEPT"
"AFTER_COMMIT_SUBMIT"

Screenshots
If applicable, add screenshots to help explain your problem.

Additional context

The root cause of this problem is that the first submit!'s aasm_transaction raises an exception originated from nested transaction (after_commit_complete) and fails to call aasm_execute_after_commit in the code below.

begin
success = if options[:persist] && use_transactions?(state_machine_name)
aasm_transaction(requires_new?(state_machine_name), requires_lock?(state_machine_name)) do
super
end
else
super
end
if success && !(event.options.keys & [:after_commit, :after_all_commits]).empty?
aasm_execute_after_commit do
event.fire_callbacks(:after_commit, self, *args)
event.fire_global_callbacks(:after_all_commits, self, *args)
end
end
success
ensure
event.fire_callbacks(:after_transaction, self, *args)
event.fire_global_callbacks(:after_all_transactions, self, *args)
end

If everyone sees this as an issue to be handled, I will work on a PR.

cc. @njw1204

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions