Merge pull request #4531 from exviva/pessimistic_with_lock
Add ActiveRecord::Base#with_lock
This commit is contained in:
commit
0056a7512a
@ -72,6 +72,33 @@
|
||||
|
||||
## Rails 3.2.0 (unreleased) ##
|
||||
|
||||
* Added a `with_lock` method to ActiveRecord objects, which starts
|
||||
a transaction, locks the object (pessimistically) and yields to the block.
|
||||
The method takes one (optional) parameter and passes it to `lock!`.
|
||||
|
||||
Before:
|
||||
|
||||
class Order < ActiveRecord::Base
|
||||
def cancel!
|
||||
transaction do
|
||||
lock!
|
||||
# ... cancelling logic
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
After:
|
||||
|
||||
class Order < ActiveRecord::Base
|
||||
def cancel!
|
||||
with_lock do
|
||||
# ... cancelling logic
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
*Olek Janiszewski*
|
||||
|
||||
* 'on' and 'ON' boolean columns values are type casted to true
|
||||
*Santiago Pastorino*
|
||||
|
||||
@ -82,7 +109,7 @@
|
||||
Example:
|
||||
rake db:migrate SCOPE=blog
|
||||
|
||||
*Piotr Sarnacki*
|
||||
*Piotr Sarnacki*
|
||||
|
||||
* Migrations copied from engines are now scoped with engine's name,
|
||||
for example 01_create_posts.blog.rb. *Piotr Sarnacki*
|
||||
|
@ -38,6 +38,18 @@ module Locking
|
||||
# account2.save!
|
||||
# end
|
||||
#
|
||||
# You can start a transaction and acquire the lock in one go by calling
|
||||
# <tt>with_lock</tt> with a block. The block is called from within
|
||||
# a transaction, the object is already locked. Example:
|
||||
#
|
||||
# account = Account.first
|
||||
# account.with_lock do
|
||||
# # This block is called within a transaction,
|
||||
# # account is already locked.
|
||||
# account.balance -= 100
|
||||
# account.save!
|
||||
# end
|
||||
#
|
||||
# Database-specific information on row locking:
|
||||
# MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
|
||||
# PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
|
||||
@ -50,6 +62,16 @@ def lock!(lock = true)
|
||||
reload(:lock => lock) if persisted?
|
||||
self
|
||||
end
|
||||
|
||||
# Wraps the passed block in a transaction, locking the object
|
||||
# before yielding. You pass can the SQL locking clause
|
||||
# as argument (see <tt>lock!</tt>).
|
||||
def with_lock(lock = true)
|
||||
transaction do
|
||||
lock!(lock)
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -388,6 +388,26 @@ def test_sane_lock_method
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_lock_commits_transaction
|
||||
person = Person.find 1
|
||||
person.with_lock do
|
||||
person.first_name = 'fooman'
|
||||
person.save!
|
||||
end
|
||||
assert_equal 'fooman', person.reload.first_name
|
||||
end
|
||||
|
||||
def test_with_lock_rolls_back_transaction
|
||||
person = Person.find 1
|
||||
old = person.first_name
|
||||
person.with_lock do
|
||||
person.first_name = 'fooman'
|
||||
person.save!
|
||||
raise 'oops'
|
||||
end rescue nil
|
||||
assert_equal old, person.reload.first_name
|
||||
end
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
|
||||
def test_no_locks_no_wait
|
||||
first, second = duel { Person.find 1 }
|
||||
|
@ -692,6 +692,17 @@ Item.transaction do
|
||||
end
|
||||
</ruby>
|
||||
|
||||
If you already have an instance of your model, you can start a transaction and acquire the lock in one go using the following code:
|
||||
|
||||
<ruby>
|
||||
item = Item.first
|
||||
item.with_lock do
|
||||
# This block is called within a transaction,
|
||||
# item is already locked.
|
||||
item.increment!(:views)
|
||||
end
|
||||
</ruby>
|
||||
|
||||
h3. Joining Tables
|
||||
|
||||
Active Record provides a finder method called +joins+ for specifying +JOIN+ clauses on the resulting SQL. There are multiple ways to use the +joins+ method.
|
||||
|
Loading…
Reference in New Issue
Block a user