2016-08-11 147 views
0

我有幾個同一個實體(用戶)的Resque worker。成功處理後,應該減少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

第一種選擇是使用鎖定optimisticpessimistic)。文檔解釋了它們的差異,你可以選擇適合你的案例。此外,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 

第二種選擇是使用原始SQL字符串查詢來增加calls_left字段。底層數據庫將處理原子更新。


最後但並非最不重要的是,您可以使用decrement!(:calls_left)方法使代碼更具可讀性。

+0

鎖不起作用。樂觀的人不能工作,因爲它被設計爲在單一過程中工作。悲觀的 - 只是沒有。 原始SQL似乎工作,謝謝。 –

+0

實際上,Rails通過向表中添加'lock_version'字段來實現樂觀鎖定(與數據庫級別樂觀鎖定略有不同)。因此,如果它們在SQL更新查詢中檢查鎖定版本,它將在非單一過程方案中起作用。但是,我不確定,沒有查看源代碼。你嘗試過使用樂觀鎖嗎?如果你這樣做,它不起作用,請讓我知道。今後注意這一點很好。 – Uzbekjon

+0

樂觀鎖定默認打開。如果對象已過時,它會引發'搶救ActiveRecord :: StaleObjectError',在我的情況中不是這樣。 這是來自文檔: '這個鎖定機制將在單個Ruby進程中運行。爲了使其適用於所有Web請求,推薦的方法是將lock_version作爲隱藏字段添加到您的表單中 –