使用排队系统进行后续属性计算

Kos*_*tas 5 ruby queue resque ruby-on-rails-3

对于以下所有假设:

  • rails v3.0
  • 红宝石v1.9
  • resque

我们有3个型号:

  • 产品 belongs_to:sku,belongs_to:category
  • Sku has_many:products,belongs_to:category
  • 类别 has_many:products,has_many:skus

当我们更新产品时(假设我们禁用它),我们需要在相关的sku和类别中发生一些事情.更新sku时也是如此.

实现这一目标的正确方法是after_save在每个模型上触发其他模型的更新事件.

例:

products.each(&:disable!)
# after_save triggers self.sku.products_updated
#   and self.category.products_updated (self is product)
Run Code Online (Sandbox Code Playgroud)

现在,如果我们有5000种产品,我们就可以享用.相同的类别可能会更新数百次,并在执行此操作时占用数据库.

我们还有一个很好的排队系统,所以更实际的更新产品的方法products.each(&:queue_disable!)就是将5000个新任务简单地投入到工作队列中.但是仍然存在5000类别更新的问题.

有没有办法避免数据库上的所有更新?

我们如何连接队列中每个类别的所有category.products_updated?

tee*_*tee 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
Run Code Online (Sandbox Code Playgroud)

如果太慢,请将其移至 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
Run Code Online (Sandbox Code Playgroud)

对其他模型回调执行类似的操作。