超シンプルに言います。

  * 求人サービスだとします
  * ユーザーは法律の関係で週に40時間未満しか働けないので、応募採用時に40時間未満かチェックしないといけない

以下採用時の処理です。

```

    ActiveRecord::Base.transaction do
      check(user) # ユーザーの勤務時間が40時間未満かチェック、超えていた場合raiseする
    
      # 中略しますが、いろんな処理をする
    
      employ(user) # ユーザーを雇用する、具体的にはuserに紐づくapplication(応募)というレコードのstatusカラムを:employedにする等の処理が行われる
    end
    
```

しかし上記だと問題があります。  
もし同時に別の会社から同ユーザーが採用ボタンを押された場合、両プロセスで、check(user)を通過してしまう可能性があります。

排他ロックをすればいいのかなーと思って調べたのですが、  
よく銀行の例えが用いられますが、ネットを調べてるとあくまで一つのレコードの更新時に、複数のプロセスから更新されないようにロックする話しか出てこないんですんね。  
（例: [銀行の特定の人の口座レコード](https://reona.dev/posts/20210616)）

今回のケース、複数の会社から採用される場合というのは、応募のレコードはあくまでそれぞれ別のレコードなんですよね。

そこで以下のように排他ロックを追加すればよいのかなと思いました。

```

    ActiveRecord::Base.transaction do
      # ユーザーの全application(応募)をロックします。
      Application.lock.where(user_id:  user_id ).lock('FOR UPDATE NOWAIT')
    
      check(user)  
    
      # 中略しますが、いろんな処理をする
    
      employ(user) # ユーザーを雇用する、具体的にはuserに紐づくapplication(応募)というレコードのstatusカラムを:employedにする等の処理が行われる
    end
    
```

ユーザーの全ての応募をロックしてしまいます。  
そうすることで、他の会社が同ユーザーを採用しようとしても、同ユーザーの全ての応募はロックされていて読み取れないので、弾くことができるのではと思いました。

このような排他ロックの使い方は正しいでしょうか？
CC BY-SA 4.0
2022-03-10T23:38:49.593
86790
2022-03-11T05:39:00.520
2022-03-11T05:39:00.520
40650
40650
question
ruby-on-rails rails-activerecord
railsの排他ロックについて