модуль ActiveRecord::Locking::Pessimistic
Locking::Pessimistic предоставляет поддержку блокировки строк с помощью SELECT … FOR UPDATE и других типов блокировок.
Цепочка ActiveRecord::Base#find к ActiveRecord::QueryMethods#lock позволяет получить эксклюзивную блокировку выбранных строк:
# select * from accounts where id=1 for update Account.lock.find(1)
Вызов lock('some locking clause') используется для применения специфичного для базы данных предложения блокировки, например, 'LOCK IN SHARE MODE' или 'FOR UPDATE NOWAIT'. Пример:
Account.transaction do
# select * from accounts where name = 'shugo' limit 1 for update nowait
shugo = Account.lock("FOR UPDATE NOWAIT").find_by(name: "shugo")
yuko = Account.lock("FOR UPDATE NOWAIT").find_by(name: "yuko")
shugo.balance -= 100
shugo.save!
yuko.balance += 100
yuko.save!
end
Вы также можете использовать метод ActiveRecord::Base#lock! для блокировки одной записи по идентификатору. Это может быть предпочтительнее, если вам не нужно блокировать каждую строку. Пример:
Account.transaction do
# select * from accounts where ...
accounts = Account.where(...)
account1 = accounts.detect { |account| ... }
account2 = accounts.detect { |account| ... }
# select * from accounts where id=? for update
account1.lock!
account2.lock!
account1.balance -= 100
account1.save!
account2.balance += 100
account2.save!
end Вы можете начать транзакцию и получить блокировку за один раз, вызвав with_lock с блоком. Блок вызывается внутри транзакции, объект уже заблокирован. Пример:
account = Account.first account.with_lock do # This block is called within a transaction, # account is already locked. account.balance -= 100 account.save! end
Информация о блокировке строк, специфичная для базы данных:
Открытые методы экземпляров
# File activerecord/lib/active_record/locking/pessimistic.rb, line 67
def lock!(lock = true)
if persisted?
if has_changes_to_save?
raise(<<-MSG.squish)
Locking a record with unpersisted changes is not supported. Use
`save` to persist the changes, or `reload` to discard them
explicitly.
MSG
end
reload(lock: lock)
end
self
end Получить блокировку строки для этой записи. Перезагружает запись, чтобы получить запрошенную блокировку. Передайте предложение блокировки SQL для добавления в конец оператора SELECT или передайте true для “FOR UPDATE” (по умолчанию, эксклюзивная блокировка строки). Возвращает заблокированную запись.
# File activerecord/lib/active_record/locking/pessimistic.rb, line 85
def with_lock(lock = true)
transaction do
lock!(lock)
yield
end
end Оборачивает переданный блок в транзакцию, блокируя объект перед передачей. Вы можете передать предложение блокировки SQL в качестве аргумента (см. lock!).
© 2004–2020 David Heinemeier Hansson
Licensed under the MIT License.