Posted at

Railsで悲観的ロック(PostgreSQLの行レベルロック)

More than 3 years have passed since last update.

悲観的ロックについてよく知らなかったので調べました。


前提条件

Rails 4.2.4

PostgreSQL 9.4


使い方

http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

findする前にlockをつけるだけ。FOR UPDATEというロック処理句が発行される。

# SELECT * FROM accounts WHERE id=1 FOR UPDATE

Account.lock.find(1)

トランザクションがはられている間ロックする。

Account.transaction do

account = Account.lock.first
account.balance -= 100
account.save!
end

ロックとトランザクションを同時にすることもできる。

account = Account.first

account.with_lock do
account.balance -= 100
account.save!
end


動作確認

rails consoleを2つ用意する。

console 1

account = Account.first

account.with_lock do
sleep 30
end

console 2

Account.lock.first

なお、console 2でhoge.firstとした場合はレコードを取得できるがhoge.first.update(...)としたときはロックが解除されるのを待つ。

というのも、

https://www.postgresql.jp/document/9.4/html/explicit-locking.html#LOCKING-ROWS


行レベルロックは、データの問い合わせには影響を与えません。 行レベルロックは、同じ行に対する書き込みとロックだけをブロックします。


という仕様のためらしい。


注意

同一トランザクション内でロックしながらいろいろレコードいじったりするとデッドロックになる可能性があるので注意したほうがよさそう。


参考