行ロックの場合、with_lockを使用できるが、
RailsのActiveRecordはライブラリとしてテーブルロックはサポートしていないみたい(多分)
データベースエンジン毎で明示的にテーブルロックを書く必要がある。
PostgreSQLの場合、テーブルレベルロックモードでEXCLUSIVE MODEを指定する。
Userテーブルロックをロックする場合、transaction内で宣言する。
今回の例はUserだけど、Userに紐づくテーブルとかのほうが実際の業務ではおおいかもしれない。(例がわるかった)
1 2 3 4 5 |
User.transaction do ActiveRecord::Base.connection.execute('LOCK users IN ACCESS EXCLUSIVE MODE') user = User.create(email: user_params[:email], app: User.avaliable_app_id) end |
ACCESS EXCLUSIVEはSELECT文すらもブロックするので、
確実に一貫性を担保したいときにはこれしかない。
Rails側でユニークのバリデーションを行った場合、pumaとかマルチスレッドで同時リクエストされると
uniqバリデーションを通り抜けることがかなりの頻度でおきるのでこういった対応が必要になる。
全てのモードのロック(ACCESS SHARE、ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、および ACCESS EXCLUSIVE)と競合します。 このモードにより、その保持者以外にテーブルにアクセスするトランザクションがないことが保証されます。
詳しくは公式をどぞ
明示的なロックについても
公式がわかりやすい。
じゃあね〜〜。