对于以下所有假设:
- 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
对其他模型回调执行类似的操作。