ActiveRecord::Locking::Pessimisticで用意されているメソッドの使い方
この記事について
ActiveRecordのモジュールActiveRecord::Locking::Pessimistic
について調べたことと使い方についてまとめます。
Railsのバージョン5系で挙動を確認していて、ドキュメントは5.2.2を参考にしています。
ActiveRecord::Locking::Pessimistic
を使うことでできるようになること
ActiveRecord::Locking::Pessimistic
はロジックに悲観的ロックをかける機能を提供してくれます。
悲観的ロックをかけることによって、口座間の送金ロジックなど原子性が求められる(不可分でなければならない)処理の信頼をより担保してくれます。
ただトランザクションをかけるだけだと、コミット前の情報に干渉することはできないにせよ、selectでコミット前のデータを取ることはできてしまいます。
悲観的ロックをかけたレコードをselectで取りに行くと、トランザクションがcommitされるまで待ってくれるので、確実に最新のデータを取ってくることができるようになります。
ActiveRecord::Locking::Pessimistic
で用意されているメソッド
# id = 1のレコードに悲観的ロックをかける Account.lock.find(1)
Account.transaction do # select * from accounts where ... accounts = Account.where(...) account1 = accounts.detect { |account| ... } account2 = accounts.detect { |account| ... } # 同じく1レコードに悲観的ロックをかける # lockメソッドに例外処理などの機能を盛り込んだメソッド account1.lock! account2.lock! account1.balance -= 100 account1.save! account2.balance += 100 account2.save! end
account = Account.first # トランザクションの中でlock!メソッドを読んでくれる account.with_lock do account.balance -= 100 account.save! end
上記はapiリファレンスからの抜粋なのですが、SQLの悲観的ロックを知らなかったため理解に手こずりました(今も概念的にしか理解できていない) transactionだけ貼っておけば原子性の求められる処理は安心!と考えていたのですが、transactionで処理中のデータに対してもselect文を走らせることができてしまうことを考慮に入れて、必要に応じて悲観的ロックを使っていきたいと思います。