Skip to content

Commit

Permalink
Fix logic on disabling commit callbacks
Browse files Browse the repository at this point in the history
Commit callbacks are intentionally disabled when errors occur when calling the callback chain in order to reset the internal record state. However, the implicit order of operations on the logic for checking if callbacks are disabled is wrong. The result is that callbacks can be unexpectedly when errors occur in transactions.
  • Loading branch information
bdurand committed May 4, 2018
1 parent 98c1432 commit a779b1a
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 1 deletion.
4 changes: 4 additions & 0 deletions activerecord/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* Fix logic on disabling commit callbacks so they are not called unexpectedly when errors occur.

*Brian Durand*

* Ensure `Associations::CollectionAssociation#size` and `Associations::CollectionAssociation#empty?`
use loaded association ids if present.

Expand Down
2 changes: 1 addition & 1 deletion activerecord/lib/active_record/transactions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ def before_committed! # :nodoc:
# Ensure that it is not called if the object was never persisted (failed create),
# but call it after the commit of a destroyed object.
def committed!(should_run_callbacks: true) #:nodoc:
if should_run_callbacks && destroyed? || persisted?
if should_run_callbacks && (destroyed? || persisted?)
_run_commit_without_transaction_enrollment_callbacks
_run_commit_callbacks
end
Expand Down
20 changes: 20 additions & 0 deletions activerecord/test/cases/transaction_callbacks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,26 @@ def test_after_commit_callbacks_should_validate_on_condition
assert_match(/:on conditions for after_commit and after_rollback callbacks have to be one of \[:create, :destroy, :update\]/, e.message)
end

def test_after_commit_chain_not_called_on_errors
record_1 = TopicWithCallbacks.create!
record_2 = TopicWithCallbacks.create!
record_3 = TopicWithCallbacks.create!
callbacks = []
record_1.after_commit_block { raise }
record_2.after_commit_block { callbacks << record_2.id }
record_3.after_commit_block { callbacks << record_3.id }
begin
TopicWithCallbacks.transaction do
record_1.save!
record_2.save!
record_3.save!
end
rescue
# From record_1.after_commit
end
assert_equal [], callbacks
end

def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_call_callbacks_on_the_parent_object
pet = Pet.first
owner = pet.owner
Expand Down

0 comments on commit a779b1a

Please sign in to comment.