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

时间:2012-03-12 08:49:34

标签: ruby ruby-on-rails-3 queue resque

对于以下所有假设:

  • rails v3.0
  • ruby​​ 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)

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

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

有没有办法避免db上的所有更新?

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

2 个答案:

答案 0 :(得分:2)

您可以使用一对Resque插件确保所有产品的单一类别更新:Resque Unique JobResque Scheduler

延迟执行作业以稍微更新类别(无论多长时间通常调用所有产品更新)并通过包含唯一作业模块来确保每个作业都是唯一的。唯一作业使用作业的参数,因此如果您尝试使用category_id 123对2个作业进行排队,则会忽略第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

答案 1 :(得分: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

为其他模型回调做类似的事情。