2012-03-12 52 views
5

下列所有的承擔這些:因之屬性計算與排隊系統

  • 軌3.0
  • 紅寶石V1.9
  • resque

我們有3種型號:

  • 產品 belongs_to的:SKU,belongs_to的:類
  • SKU的has_many:產品,belongs_to的:類
  • 類別的has_many:產品的has_many:單品

當我們更新產品(比方說,我們禁用它)我們需要有一些事情發生在相關的SKU和類別上。 sku更新時也是如此。

正確實現此目的的方法是在觸發其他模型的更新事件的每個模型上都有一個after_save

例如:

products.each(&:disable!) 
# after_save triggers self.sku.products_updated 
# and self.category.products_updated (self is product) 

現在,如果我們有5000個產品,我們是在爲一種享受。同一類別可能會更新數百次,並在此過程中佔用數據庫。

我們也有一個很好的排隊系統,所以更新的產品更實際的方法是products.each(&:queue_disable!),它可以簡單地將5000個新任務投入工作隊列。儘管如此,5000類別更新的問題仍然存在。

有沒有辦法避免db上的所有更新?

我們如何連接隊列中每個類別的所有category.products_updated?

+0

爲什麼在產品更換時需要更新類別?它是一個平均價格還是什麼?如果是這樣,請刪除計數器緩存,並在需要時或使用http://redis.io/進行計算。 – 2012-03-23 18:24:51

回答

0

在單個SQL調用中執行依賴更新。 #update_all將一次更新多條記錄。例如,

在after_update回調,更新所有相關的列值:

class Category 
    after_update :update_dependent_products 

    def update_dependent_products 
    products.update_all(disabled: disabled?) if disabled_changed? 
    end 
end 

如果是這樣的速度太慢,將其移動到resque工作:

class Category 
    after_update :queue_update_dependent_products 

    def update_dependent_products 
    products.update_all(disabled: disabled?) if disabled_changed? 
    end  

    def queue_update_dependent_products 
    Resque.enqueue(Jobs::UpdateCategoryDependencies, self.id) if disabled_changed? 
    end 
end 

class Jobs::UpdateCategoryDependencies 
    def self.perform(category_id) 
    category = Category.find_by_id(category_id) 
    category.update_dependent_products if category 
    end 
end 

做了類似的事情其他模型回調。

+0

我試圖做的事情是相反的:我更新了一大堆產品,每次更新都要傳播到該類別,但如果所有產品更新都是先完成的,然後每個受影響類別只進行一次更新。 – Kostas 2012-03-29 13:56:44

+0

好吧,我誤解你在問什麼。我添加了另一個答案,並將離開這一個。 – tee 2012-03-29 16:22:23

2

通過使用幾個Resque插件,您可以確保所有產品的單個類別更新:Resque Unique JobResque Scheduler

延遲作業執行以稍微更新類別(通常需要很長時間才能調用所有產品更新),並通過包含唯一作業模塊確保每個作業都是唯一的。唯一作業使用作業的參數,因此如果嘗試使用category_id 123排隊2個作業,它將忽略第二個作業,因爲作業已排隊。

class Product 
    after_save :queue_category_update 

    def queue_category_update 
    Resque.enqueue_at(1.minute.from_now, Jobs::UpdateCategory, category.id) if need_to_update_category? 
    end 
end 

module Jobs 
    module UpdateCategory 
    include Resque::Plugins::UniqueJob 

    def self.perform(category_id) 
     category = Category.find_by_id(category_id) 
     category.update_some_stuff if category 
    end 
    end 
end