mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Accept optional transaction args to #with_lock
Make #with_lock as expressive as calling #transaction and #lock! individually to enable behavior like so: person.with_lock("FOR UPDATE NOWAIT", requires_new: true) do ... end Helps teams who prefer #with_lock over #lock! to ensure the lock is taken out within a transaction, even when advanced transaction control is required without requiring redundant transaction blocks.
This commit is contained in:
parent
6d42731ded
commit
b5e670a9dd
3 changed files with 43 additions and 3 deletions
|
@ -1,3 +1,8 @@
|
|||
* Accept optional transaction args to `ActiveRecord::Locking::Pessimistic#with_lock`
|
||||
|
||||
`#with_lock` now accepts transaction options like `requires_new:`,
|
||||
`isolation:`, and `joinable:`
|
||||
|
||||
* Adds support for deferrable foreign key constraints in PostgreSQL.
|
||||
|
||||
By default, foreign key constraints in PostgreSQL are checked after each statement. This works for most use cases,
|
||||
|
|
|
@ -81,9 +81,15 @@ module ActiveRecord
|
|||
|
||||
# Wraps the passed block in a transaction, locking the object
|
||||
# before yielding. You can pass the SQL locking clause
|
||||
# as argument (see <tt>lock!</tt>).
|
||||
def with_lock(lock = true)
|
||||
transaction do
|
||||
# as an optional argument (see <tt>#lock!</tt>).
|
||||
#
|
||||
# You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
|
||||
# and <tt>joinable:</tt> to the wrapping transaction (see
|
||||
# <tt>ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction</tt>).
|
||||
def with_lock(*args)
|
||||
transaction_opts = args.extract_options!
|
||||
lock = args.present? ? args.first : true
|
||||
transaction(**transaction_opts) do
|
||||
lock!(lock)
|
||||
yield
|
||||
end
|
||||
|
|
|
@ -738,6 +738,19 @@ unless in_memory_db?
|
|||
assert_equal old, person.reload.first_name
|
||||
end
|
||||
|
||||
def test_with_lock_configures_transaction
|
||||
person = Person.find 1
|
||||
Person.transaction do
|
||||
outer_transaction = Person.connection.transaction_manager.current_transaction
|
||||
assert_equal true, outer_transaction.joinable?
|
||||
person.with_lock(requires_new: true, joinable: false) do
|
||||
current_transaction = Person.connection.transaction_manager.current_transaction
|
||||
assert_not_equal outer_transaction, current_transaction
|
||||
assert_equal false, current_transaction.joinable?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
def test_lock_sending_custom_lock_statement
|
||||
Person.transaction do
|
||||
|
@ -747,6 +760,22 @@ unless in_memory_db?
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_lock_sets_isolation
|
||||
person = Person.find 1
|
||||
person.with_lock(isolation: :read_uncommitted) do
|
||||
current_transaction = Person.connection.transaction_manager.current_transaction
|
||||
assert_equal :read_uncommitted, current_transaction.isolation_level
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_lock_locks_with_no_args
|
||||
person = Person.find 1
|
||||
assert_sql(/LIMIT \$?\d FOR UPDATE/i) do
|
||||
person.with_lock do
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_locks_no_wait
|
||||
|
|
Loading…
Reference in a new issue