2015-05-14 47 views
1

爲了異步處理事件並創建活動源,我使用了Sidekiq和Ruby on Rails的全局ID。使用Sidekiq執行異步作業時處理過時的數據

這適用於大多數類型的活動,但其中一些需要的數據可能隨着作業執行時間而改變。

這裏是一個完全虛構的例子:

class Movie < ActiveRecord::Base 
    include Redis::Objects 
    value :score # stores an integer in Redis 

    has_many :likes 

    def popular? 
    likes.count > 1000 
    end 
end 

而一個Sidekiq工人每一個電影被更新時進行工作:

class MovieUpdatedWorker 
    include Sidekiq::Worker 

    def perform(global_id) 
    movie = GlobalID::Locator.locate(global_id) 
    MovieUpdatedActivity.create(movie: movie, score: movie.score) if movie.popular? 
    end 
end 

現在,想象Sidekiq相對滯後,在此之前它有機會執行它的工作,電影的score在Redis中更新,一些用戶不喜歡該電影,並且popular方法現在返回false。

Sidekiq最終使用更新的數據。

我正在尋找安排作業的方式,同時確保執行作業時所需的數據不會更改。有幾個想法:

1 /手動傳遞所需的所有數據,並相應地調整工人:

MovieUpdatedWorker.perform_async(
    movie: self, 
    score: score, 
    likes_count: likes.count 
) 

這可以工作,但將需要重新實現/複製依賴於諸如scorepopular?數據的所有方法(想象一個比這兩個/三個可移動部件多得多的應用程序)。

由於序列化對象在Redis中可能佔用很多空間,所以這並不能很好地擴展。

2 /存根上的工人通過記錄一些方法:

MovieUpdatedWorker.perform_async(
    global_id, 
    stubs: { score: score, popular?: popular? } 
) 

class MovieUpdatedWorker 
    include Sidekiq::Worker 

    def perform(global_id, stubs: {}) 
    movie = GlobalID::Locator.locate(global_id) 

    # inspired by RSpec 
    stubs.each do |message, return_value| 
     movie.stub(message) { return_value } 
    end 

    MovieUpdatedActivity.create(movie: movie, score: movie.score) if movie.popular? 
    end 
end 

這不是功能性的,但你能想象處理實際記錄的方便,不必重新實現現有方法,並處理實際的數據。

您是否看到過其他策略來「凍結」對象數據並對其進行異步處理?你怎麼看待這兩個?

回答

0

我不會說數據是陳舊的,因爲你實際上會擁有它的最新版本,只是它不再受歡迎。這聽起來像你真的想要陳舊的版本。

如果您不希望數據發生變化,您需要以某種方式緩存它。或者像您說的那樣,直接將數據傳遞給作業,或者您可以在數據庫中添加某種形式的數據版本控制,並將引用傳遞給舊版本。

我認爲將您需要的數據傳遞給Redis是一種合理的方式。你只能序列化你真正關心的屬性,比如分數。

+0

你說得對,我修改了標題並改寫了問題。謝謝! –