超シンプルに言います。
- 求人サービスだとします
- ユーザーは法律の関係で週に40時間未満しか働けないので、応募採用時に40時間未満かチェックしないといけない
以下採用時の処理です。
ruby
1ActiveRecord::Base.transaction do 2 check(user) # ユーザーの勤務時間が40時間未満かチェック、超えていた場合raiseする 3 4 # 中略しますが、いろんな処理をする 5 6 employ(user) # ユーザーを雇用する、具体的にはuserに紐づくapplication(応募)というレコードのstatusカラムを:employedにする等の処理が行われる 7end
しかし上記だと問題があります。
もし同時に別の会社から同ユーザーが採用ボタンを押された場合、両プロセスで、check(user)を通過してしまう可能性があります。
排他ロックをすればいいのかなーと思って調べたのですが、
よく銀行の例えが用いられますが、ネットを調べてるとあくまで一つのレコードの更新時に、複数のプロセスから更新されないようにロックする話しか出てこないんですんね。
(例: 銀行の特定の人の口座レコード)
今回のケース、複数の会社から採用される場合というのは、応募のレコードはあくまでそれぞれ別のレコードなんですよね。
そこで以下のように排他ロックを追加すればよいのかなと思いました。
ruby
1ActiveRecord::Base.transaction do 2 # ユーザーの全application(応募)をロックします。 3 Application.lock.where(user_id: user_id ).lock('FOR UPDATE NOWAIT') 4 5 check(user) 6 7 # 中略しますが、いろんな処理をする 8 9 employ(user) # ユーザーを雇用する、具体的にはuserに紐づくapplication(応募)というレコードのstatusカラムを:employedにする等の処理が行われる 10end
ユーザーの全ての応募をロックしてしまいます。
そうすることで、他の会社が同ユーザーを採用しようとしても、同ユーザーの全ての応募はロックされていて読み取れないので、弾くことができるのではと思いました。
このような排他ロックの使い方は正しいでしょうか?
回答2件
下記のような回答は推奨されていません。
このような回答には修正を依頼しましょう。