質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Active Record

Active Recordは、一つのオブジェクトに対しドメインのロジックとストレージの抽象性を結合するデザインパターンです。

Q&A

解決済

2回答

1386閲覧

このような排他ロックの使い方は本来の使い方ではない?おかしいでしょうか?

thesnowman

総合スコア154

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Active Record

Active Recordは、一つのオブジェクトに対しドメインのロジックとストレージの抽象性を結合するデザインパターンです。

0グッド

0クリップ

投稿2022/03/10 22:58

編集2022/03/11 05:39

超シンプルに言います。

  • 求人サービスだとします
  • ユーザーは法律の関係で週に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

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

このような排他ロックの使い方は正しいでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

排他制御は、週40時間のチェックだけではなく、一人のユーザーが同じ日時に複数の求人に応募した場合とか、1名募集の求人に対して二人が同時に応募した場合など、ありとあらゆるケースで必要です。
まずは、排他制御を楽観的または悲観的のどちらでおこなうか戦略を決めます。
https://qiita.com/NagaokaKenichi/items/73040df85b7bd4e9ecfc

RoRには、楽観的ロック、悲観的ロックどらも、仕組みがあるので、それを活用しましょう。質問者さんのコードは、悲観的ロックを使ったものですね。
https://qiita.com/iizukapynyo/items/2e5516ee2f1c9b324dd4
https://qiita.com/Masashi9410/items/c116a0901401f16f59c9

悲観的ロックを使用するなら、そのトランザクションで更新するテーブルは、すべてロックするのが原則です。

投稿2022/03/11 02:33

nukasa

総合スコア406

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

あくまで一案ですが、
応募の状態管理をもう少し細かく分けて、
仮の応募中と、応募中確定と、応募確定とを分けたら、
そういう時間の計算もしやすくなると思います。

仮の応募中は現行トランザクション内のみ有効ですぐ取り消されるような、
今応募したら時間がいくらになるのかを集計するためだけに使うものとして
扱って、集計後に手動で状態をもとに戻すなどすればよいかと。

集計後に確実にロールバックできる前提に立てば、
状態を分けなくても、一旦応募中の状態にして集計してから、ロールバックすることもできそうです。

投稿2022/03/11 00:08

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

thesnowman

2022/03/11 00:26 編集

ありがとうございます! その解決策は全く想定していませんでした、非常に感謝です。 ちなみに、排他ロックだとまずい理由ってあったりしますか? 個人的にはロックしてる間、同ユーザーの全application(応募)レコードが読み込み待ちになるので、想定していない所に影響しそうで怖さはあります。
退会済みユーザー

退会済みユーザー

2022/03/11 00:34

ロックに頼るとロックに挫けると言いますか、 排他ロックで乗り越えてしまうと、それで乗り越えられない壁にぶち当たるのが、世の常なので、 その先の先まで見据えて設計できるならチャレンジしても良いでしょうけれど。 特定の処理や仕組みに依存しないアルゴリズムが組めると良いですよね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問