Skip to content

Commit 78e7a7f

Browse files
authored
Merge pull request rails#43224 from jmileham/with_lock_accepts_transaction_opts
Accept optional transaction args to #with_lock
2 parents 6d42731 + b5e670a commit 78e7a7f

File tree

3 files changed

+43
-3
lines changed

3 files changed

+43
-3
lines changed

activerecord/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
* Accept optional transaction args to `ActiveRecord::Locking::Pessimistic#with_lock`
2+
3+
`#with_lock` now accepts transaction options like `requires_new:`,
4+
`isolation:`, and `joinable:`
5+
16
* Adds support for deferrable foreign key constraints in PostgreSQL.
27

38
By default, foreign key constraints in PostgreSQL are checked after each statement. This works for most use cases,

activerecord/lib/active_record/locking/pessimistic.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,15 @@ def lock!(lock = true)
8181

8282
# Wraps the passed block in a transaction, locking the object
8383
# before yielding. You can pass the SQL locking clause
84-
# as argument (see <tt>lock!</tt>).
85-
def with_lock(lock = true)
86-
transaction do
84+
# as an optional argument (see <tt>#lock!</tt>).
85+
#
86+
# You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
87+
# and <tt>joinable:</tt> to the wrapping transaction (see
88+
# <tt>ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction</tt>).
89+
def with_lock(*args)
90+
transaction_opts = args.extract_options!
91+
lock = args.present? ? args.first : true
92+
transaction(**transaction_opts) do
8793
lock!(lock)
8894
yield
8995
end

activerecord/test/cases/locking_test.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,19 @@ def test_with_lock_rolls_back_transaction
738738
assert_equal old, person.reload.first_name
739739
end
740740

741+
def test_with_lock_configures_transaction
742+
person = Person.find 1
743+
Person.transaction do
744+
outer_transaction = Person.connection.transaction_manager.current_transaction
745+
assert_equal true, outer_transaction.joinable?
746+
person.with_lock(requires_new: true, joinable: false) do
747+
current_transaction = Person.connection.transaction_manager.current_transaction
748+
assert_not_equal outer_transaction, current_transaction
749+
assert_equal false, current_transaction.joinable?
750+
end
751+
end
752+
end
753+
741754
if current_adapter?(:PostgreSQLAdapter)
742755
def test_lock_sending_custom_lock_statement
743756
Person.transaction do
@@ -747,6 +760,22 @@ def test_lock_sending_custom_lock_statement
747760
end
748761
end
749762
end
763+
764+
def test_with_lock_sets_isolation
765+
person = Person.find 1
766+
person.with_lock(isolation: :read_uncommitted) do
767+
current_transaction = Person.connection.transaction_manager.current_transaction
768+
assert_equal :read_uncommitted, current_transaction.isolation_level
769+
end
770+
end
771+
772+
def test_with_lock_locks_with_no_args
773+
person = Person.find 1
774+
assert_sql(/LIMIT \$?\d FOR UPDATE/i) do
775+
person.with_lock do
776+
end
777+
end
778+
end
750779
end
751780

752781
def test_no_locks_no_wait

0 commit comments

Comments
 (0)