Skip to content

Commit eccc606

Browse files
committed
Remove deprecated behavior that would rollback a transaction block when exited using return, break or throw.
1 parent c313d91 commit eccc606

File tree

8 files changed

+28
-114
lines changed

8 files changed

+28
-114
lines changed

activerecord/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
* Remove deprecated behavior that would rollback a transaction block when exited using `return`, `break` or `throw`.
2+
3+
*Rafael Mendonça França*
4+
5+
* Deprecate `Rails.application.config.active_record.commit_transaction_on_non_local_return`.
6+
7+
*Rafael Mendonça França*
8+
19
* Remove deprecated support to pass `rewhere` to `ActiveRecord::Relation#merge`.
210

311
*Rafael Mendonça França*

activerecord/lib/active_record.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,19 @@ def self.global_executor_concurrency # :nodoc:
333333
singleton_class.attr_accessor :run_after_transaction_callbacks_in_order_defined
334334
self.run_after_transaction_callbacks_in_order_defined = false
335335

336-
singleton_class.attr_accessor :commit_transaction_on_non_local_return
337-
self.commit_transaction_on_non_local_return = false
336+
def self.commit_transaction_on_non_local_return
337+
ActiveRecord.deprecator.warn <<-WARNING.squish
338+
`Rails.application.config.active_record.commit_transaction_on_non_local_return`
339+
is deprecated and will be removed in Rails 7.3.
340+
WARNING
341+
end
342+
343+
def self.commit_transaction_on_non_local_return=(value)
344+
ActiveRecord.deprecator.warn <<-WARNING.squish
345+
`Rails.application.config.active_record.commit_transaction_on_non_local_return`
346+
is deprecated and will be removed in Rails 7.3.
347+
WARNING
348+
end
338349

339350
##
340351
# :singleton-method: warn_on_records_fetched_greater_than

activerecord/lib/active_record/connection_adapters/abstract/transaction.rb

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,6 @@ def within_new_transaction(isolation: nil, joinable: true)
534534
transaction = begin_transaction(isolation: isolation, joinable: joinable)
535535
begin
536536
ret = yield
537-
completed = true
538537
ret
539538
rescue Exception => error
540539
rollback_transaction
@@ -543,24 +542,8 @@ def within_new_transaction(isolation: nil, joinable: true)
543542
raise
544543
ensure
545544
unless error
546-
# In 7.1 we enforce timeout >= 0.4.0 which no longer use throw, so we can
547-
# go back to the original behavior of committing on non-local return.
548-
# If users are using throw, we assume it's not an error case.
549-
completed = true if ActiveRecord.commit_transaction_on_non_local_return
550-
551545
if Thread.current.status == "aborting"
552546
rollback_transaction
553-
elsif !completed && transaction.written
554-
ActiveRecord.deprecator.warn(<<~EOW)
555-
A transaction is being rolled back because the transaction block was
556-
exited using `return`, `break` or `throw`.
557-
In Rails 7.2 this transaction will be committed instead.
558-
To opt-in to the new behavior now and suppress this warning
559-
you can set:
560-
561-
Rails.application.config.active_record.commit_transaction_on_non_local_return = true
562-
EOW
563-
rollback_transaction
564547
else
565548
begin
566549
commit_transaction

activerecord/test/cases/relation/merging_test.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,6 @@ def test_merge_doesnt_duplicate_same_clauses
150150
assert_queries_match(/WHERE \(#{Regexp.escape(author_id)} IN \('1'\)\)\z/) do
151151
assert_equal [david], only_david.merge(only_david)
152152
end
153-
154-
assert_queries_match(/WHERE \(#{Regexp.escape(author_id)} IN \('1'\)\)\z/) do
155-
assert_deprecated(ActiveRecord.deprecator) do
156-
assert_equal [david], only_david.merge(only_david, rewhere: true)
157-
end
158-
end
159153
else
160154
assert_queries_match(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do
161155
assert_equal [david], only_david.merge(only_david)

activerecord/test/cases/transactions_test.rb

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ class TransactionTest < ActiveRecord::TestCase
1717

1818
def setup
1919
@first, @second = Topic.find(1, 2).sort_by(&:id)
20-
@commit_transaction_on_non_local_return_was = ActiveRecord.commit_transaction_on_non_local_return
21-
end
22-
23-
def teardown
24-
ActiveRecord.commit_transaction_on_non_local_return = @commit_transaction_on_non_local_return_was
2520
end
2621

2722
def test_rollback_dirty_changes
@@ -343,49 +338,7 @@ def test_deprecation_on_ruby_timeout_outside_inner_transaction
343338
assert_predicate Topic.find(1), :approved?, "First should have been approved"
344339
end
345340

346-
def test_rollback_with_return
347-
committed = false
348-
349-
Topic.connection.class_eval do
350-
alias :real_commit_db_transaction :commit_db_transaction
351-
define_method(:commit_db_transaction) do
352-
committed = true
353-
real_commit_db_transaction
354-
end
355-
end
356-
357-
assert_deprecated(ActiveRecord.deprecator) do
358-
transaction_with_return
359-
end
360-
assert_not committed
361-
362-
assert_not_predicate Topic.find(1), :approved?
363-
assert_predicate Topic.find(2), :approved?
364-
ensure
365-
Topic.connection.class_eval do
366-
remove_method :commit_db_transaction
367-
alias :commit_db_transaction :real_commit_db_transaction rescue nil
368-
end
369-
end
370-
371-
def test_rollback_on_ruby_timeout
372-
assert_deprecated(ActiveRecord.deprecator) do
373-
catch do |timeout|
374-
Topic.transaction do
375-
@first.approved = true
376-
@first.save!
377-
378-
throw timeout
379-
end
380-
end
381-
end
382-
383-
assert_not_predicate Topic.find(1), :approved?
384-
end
385-
386-
def test_break_from_transaction_7_1_behavior
387-
ActiveRecord.commit_transaction_on_non_local_return = true
388-
341+
def test_break_from_transaction_commits
389342
@first.transaction do
390343
assert_not_predicate @first, :approved?
391344
@first.update!(approved: true)
@@ -401,9 +354,7 @@ def test_break_from_transaction_7_1_behavior
401354
assert_predicate Topic.find(2), :approved?, "Second should have been approved"
402355
end
403356

404-
def test_thow_from_transaction_7_1_behavior
405-
ActiveRecord.commit_transaction_on_non_local_return = true
406-
357+
def test_throw_from_transaction_commits
407358
catch(:not_an_error) do
408359
@first.transaction do
409360
assert_not_predicate @first, :approved?
@@ -433,9 +384,7 @@ def _test_return_from_transaction_7_1_behavior
433384
end
434385
end
435386

436-
def test_return_from_transaction_7_1_behavior
437-
ActiveRecord.commit_transaction_on_non_local_return = true
438-
387+
def test_return_from_transaction_commits
439388
_test_return_from_transaction_7_1_behavior
440389
assert_predicate Topic.find(1), :approved?, "First should have been approved"
441390
assert_predicate Topic.find(2), :approved?, "Second should have been approved"

guides/source/7_2_release_notes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,14 @@ Please refer to the [Changelog][active-record] for detailed changes.
164164

165165
* Remove deprecated support to pass `rewhere` to `ActiveRecord::Relation#merge`.
166166

167+
* Remove deprecated behavior that would rollback a transaction block when exited using `return`, `break` or `throw`.
168+
167169
### Deprecations
168170

169171
* Deprecate `Rails.application.config.active_record.allow_deprecated_singular_associations_name`
170172

173+
* Deprecate `Rails.application.config.active_record.commit_transaction_on_non_local_return`
174+
171175
### Notable changes
172176

173177
Active Storage

guides/source/configuring.md

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ Below are the default values associated with each target version. In cases of co
7171
- [`config.action_view.sanitizer_vendor`](#config-action-view-sanitizer-vendor): `Rails::HTML::Sanitizer.best_supported_vendor`
7272
- [`config.active_record.before_committed_on_all_records`](#config-active-record-before-committed-on-all-records): `true`
7373
- [`config.active_record.belongs_to_required_validates_foreign_key`](#config-active-record-belongs-to-required-validates-foreign-key): `false`
74-
- [`config.active_record.commit_transaction_on_non_local_return`](#config-active-record-commit-transaction-on-non-local-return): `true`
7574
- [`config.active_record.default_column_serializer`](#config-active-record-default-column-serializer): `nil`
7675
- [`config.active_record.encryption.hash_digest_class`](#config-active-record-encryption-hash-digest-class): `OpenSSL::Digest::SHA256`
7776
- [`config.active_record.encryption.support_sha1_for_non_deterministic_encryption`](#config-active-record-encryption-support-sha1-for-non-deterministic-encryption): `false`
@@ -1322,39 +1321,6 @@ The default value depends on the `config.load_defaults` target version:
13221321
| (original) | `false` |
13231322
| 7.0 | `true` |
13241323

1325-
#### `config.active_record.commit_transaction_on_non_local_return`
1326-
1327-
Defines whether `return`, `break` and `throw` inside a `transaction` block cause the transaction to be
1328-
committed or rolled back. e.g.:
1329-
1330-
```ruby
1331-
Model.transaction do
1332-
model.save
1333-
return
1334-
other_model.save # not executed
1335-
end
1336-
```
1337-
1338-
If set to `false`, it will be rolled back.
1339-
1340-
If set to `true`, the above transaction will be committed.
1341-
1342-
| Starting with version | The default value is |
1343-
| --------------------- | -------------------- |
1344-
| (original) | `false` |
1345-
| 7.1 | `true` |
1346-
1347-
Historically only raised errors would trigger a rollback, but in Ruby `2.3`, the `timeout` library
1348-
started using `throw` to interrupt execution which had the adverse effect of committing open transactions.
1349-
1350-
To solve this, in Active Record 6.1 the behavior was changed to instead rollback the transaction as it was safer
1351-
than to potentially commit an incomplete transaction.
1352-
1353-
Using `return`, `break` or `throw` inside a `transaction` block was essentially deprecated from Rails 6.1 onwards.
1354-
1355-
However with the release of `timeout 0.4.0`, `Timeout.timeout` now raises an error again, and Active Record is able
1356-
to return to its original, less surprising, behavior.
1357-
13581324
#### `config.active_record.raise_on_assign_to_attr_readonly`
13591325

13601326
Enable raising on assignment to attr_readonly attributes. The previous

railties/lib/rails/application/configuration.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,6 @@ def load_defaults(target_version)
279279

280280
if respond_to?(:active_record)
281281
active_record.run_commit_callbacks_on_first_saved_instances_in_transaction = false
282-
active_record.commit_transaction_on_non_local_return = true
283282
active_record.sqlite3_adapter_strict_strings_by_default = true
284283
active_record.query_log_tags_format = :sqlcommenter
285284
active_record.raise_on_assign_to_attr_readonly = true

0 commit comments

Comments
 (0)