2012-03-12 58 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