Skip to content

Commit

Permalink
Merge pull request #43224 from jmileham/with_lock_accepts_transaction…
Browse files Browse the repository at this point in the history
…_opts

Accept optional transaction args to #with_lock
  • Loading branch information
rafaelfranca committed Sep 23, 2021
2 parents 6d42731 + b5e670a commit 78e7a7f
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 3 deletions.
5 changes: 5 additions & 0 deletions activerecord/CHANGELOG.md
@@ -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,
Expand Down
12 changes: 9 additions & 3 deletions activerecord/lib/active_record/locking/pessimistic.rb
Expand Up @@ -81,9 +81,15 @@ def lock!(lock = true)

# 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
Expand Down
29 changes: 29 additions & 0 deletions activerecord/test/cases/locking_test.rb
Expand Up @@ -738,6 +738,19 @@ def test_with_lock_rolls_back_transaction
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
Expand All @@ -747,6 +760,22 @@ def test_lock_sending_custom_lock_statement
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
Expand Down

0 comments on commit 78e7a7f

Please sign in to comment.