aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Daer <jeremydaer@gmail.com>2016-02-01 14:26:12 -0700
committerJeremy Daer <jeremydaer@gmail.com>2016-02-01 14:26:12 -0700
commit633969f159c4f1ff23d98ec6e25cf84a78994824 (patch)
tree08209815635c56d0a393608284fcb0f50b718855
parentb801566972d3e23d5f299e914034bda2d44ce5f5 (diff)
parent8fd123f7ebf938d21c1a097561950c6d7e1213bb (diff)
downloadrails-633969f159c4f1ff23d98ec6e25cf84a78994824.tar.gz
rails-633969f159c4f1ff23d98ec6e25cf84a78994824.tar.bz2
rails-633969f159c4f1ff23d98ec6e25cf84a78994824.zip
Merge pull request #23407 from jeremy/corrupt-before-commit
Fix corrupt transaction state caused by `before_commit` exceptions
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/transaction.rb9
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb26
2 files changed, 33 insertions, 2 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
index 14d04a6388..6ecdab6eb0 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb
@@ -167,8 +167,13 @@ module ActiveRecord
def commit_transaction
transaction = @stack.last
- transaction.before_commit_records
- @stack.pop
+
+ begin
+ transaction.before_commit_records
+ ensure
+ @stack.pop
+ end
+
transaction.commit
transaction.commit_records
end
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 637f89196e..8a7f19293d 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -34,6 +34,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
has_many :replies, class_name: "ReplyWithCallbacks", foreign_key: "parent_id"
+ before_commit { |record| record.do_before_commit(nil) }
after_commit { |record| record.do_after_commit(nil) }
after_create_commit { |record| record.do_after_commit(:create) }
after_update_commit { |record| record.do_after_commit(:update) }
@@ -47,6 +48,12 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
@history ||= []
end
+ def before_commit_block(on = nil, &block)
+ @before_commit ||= {}
+ @before_commit[on] ||= []
+ @before_commit[on] << block
+ end
+
def after_commit_block(on = nil, &block)
@after_commit ||= {}
@after_commit[on] ||= []
@@ -59,6 +66,11 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
@after_rollback[on] << block
end
+ def do_before_commit(on)
+ blocks = @before_commit[on] if defined?(@before_commit)
+ blocks.each{|b| b.call(self)} if blocks
+ end
+
def do_after_commit(on)
blocks = @after_commit[on] if defined?(@after_commit)
blocks.each{|b| b.call(self)} if blocks
@@ -74,6 +86,20 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
@first = TopicWithCallbacks.find(1)
end
+ # FIXME: Test behavior, not implementation.
+ def test_before_commit_exception_should_pop_transaction_stack
+ @first.before_commit_block { raise 'better pop this txn from the stack!' }
+
+ original_txn = @first.class.connection.current_transaction
+
+ begin
+ @first.save!
+ fail
+ rescue
+ assert_equal original_txn, @first.class.connection.current_transaction
+ end
+ end
+
def test_call_after_commit_after_transaction_commits
@first.after_commit_block{|r| r.history << :after_commit}
@first.after_rollback_block{|r| r.history << :after_rollback}