私は同じエンティティ(ユーザー)に対して複数のResqueワーカーを持っています。処理が成功した後は、call_left
属性を減らす必要があります。ActiveJob/Resqueダーティリード。トランザクション分離レベル
perform_now
(結果的に)と完全に動作しますが、perform_later
(並行して)で予測できない結果が生じます。ログには、同じ番号のコミットがcalls_left
にあります。
reload
メソッドを使用しようとしましたが、最も高い分離レベルを設定しました。しかし、まだこの問題があります。
解決方法
class DataProcessJob < ActiveJob::Base
queue_as :default
def perform(user_id, profile_id)
User.transaction(isolation: :serializable) do
user = User.find(user_id).reload
user.data_process(profile_id)
user.update(calls_left: user.calls_left-1)
end
end
end
ロックが機能しません。楽観的なものは、単一プロセスで動作するように設計されているため動作しません。悲観的なもの - ちょうどそうではありません。 Raw SQLはうまくいくようです。 –
実際には、Railsはテーブルに 'lock_version'フィールドを追加することでオプティミスティックロックを実装します(DBレベルのオプティミスティックロックとは少し異なります)。したがって、SQL更新クエリでロックバージョンのチェックを実装すると、単一のプロセスシナリオでは機能しません。しかし、私は確信していない、ソースコードを見ていない。あなたは楽観的なロックを使用してみましたか?あなたがして、それが動作しなかった場合は、私に知らせてください。将来のためにこれを書き留めておきましょう。 – Uzbekjon
オプティミスティックロックは、デフォルトで有効になっています。私の場合、オブジェクトが古くなっている場合、 'rescue ActiveRecord :: StaleObjectError'が発生します。 これはドキュメントからです: 'このロック機構は、1つのRubyプロセス内で機能します。すべてのWebリクエストで機能するようにするには、lock_versionをフォームに隠しフィールドとして追加することをお勧めします。 –