使用排队系统进行相应的属性计算



对于以下所有假设:

  • rails v3.0
  • ruby v1.9
  • resque

我们有三种型号:

  • 产品belongs_to:sku,belongs_to:类别
  • Skuhas_many:产品,belongs_to:类别
  • 类别has_many:产品,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个类别更新的问题仍然存在。

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

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

您可以通过使用两个Resque插件:Resque Unique Job和Resque Scheduler来确保所有产品的单一类别更新。

延迟作业的执行以稍微更新类别(无论通常调用所有产品更新需要多长时间),并通过包含"唯一作业"模块来确保每个作业都是唯一的。Unique Job使用作业的参数,因此,如果您尝试将2个category_id为123的作业排队,它会忽略第二个作业,因为该作业已经排队。

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

在单个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

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

最新更新