2016-08-11 10 views
0

私は同じエンティティ(ユーザー)に対して複数の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 

答えて

1

最初のオプションは、(optimistic又はpessimistic)ロックを使用することであろう。ドキュメントでは、その違いを説明し、あなたのケースに合ったものを選ぶことができます。また、オプティミスティックロックを使用する場合は、ドキュメントのhere is a relevant code snippetが役に立ちます。

def with_optimistic_retry 
    begin 
    yield 
    rescue ActiveRecord::StaleObjectError 
    begin 
     # Reload lock_version in particular. 
     reload 
    rescue ActiveRecord::RecordNotFound 
     # If the record is gone there is nothing to do. 
    else 
     retry 
    end 
    end 
end 

第2のオプションは、生のSQL文字列クエリを使用してcalls_leftフィールドを増分することです。基盤となるDBは原子的な更新を扱うでしょう。


最後に、decrement!(:calls_left)メソッドを使用すると、コードを読みやすくすることができます。

+0

ロックが機能しません。楽観的なものは、単一プロセスで動作するように設計されているため動作しません。悲観的なもの - ちょうどそうではありません。 Raw SQLはうまくいくようです。 –

+0

実際には、Railsはテーブルに 'lock_version'フィールドを追加することでオプティミスティックロックを実装します(DBレベルのオプティミスティックロックとは少し異なります)。したがって、SQL更新クエリでロックバージョンのチェックを実装すると、単一のプロセスシナリオでは機能しません。しかし、私は確信していない、ソースコードを見ていない。あなたは楽観的なロックを使用してみましたか?あなたがして、それが動作しなかった場合は、私に知らせてください。将来のためにこれを書き留めておきましょう。 – Uzbekjon

+0

オプティミスティックロックは、デフォルトで有効になっています。私の場合、オブジェクトが古くなっている場合、 'rescue ActiveRecord :: StaleObjectError'が発生します。 これはドキュメントからです: 'このロック機構は、1つのRubyプロセス内で機能します。すべてのWebリクエストで機能するようにするには、lock_versionをフォームに隠しフィールドとして追加することをお勧めします。 –

関連する問題