Последующие вычисления атрибутов с помощью системы очередей - PullRequest
5 голосов
/ 12 марта 2012

Для всех следующих предположений:

  • рельсы v3.0
  • ruby ​​v1.9
  • спасательное

У нас есть 3 модели:

  • Продукт принадлежит_ к: sku, принадлежит_ к: категории
  • Sku has_many: продукты, принадлежащие к: категории
  • Категория 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 для каждой категории в очереди?

Ответы [ 2 ]

2 голосов
/ 29 марта 2012

Вы можете обеспечить обновление одной категории для всех продуктов, используя пару плагинов Resque: Resque Unique Job и Resque Scheduler .

Отложите выполнение задания, чтобы немного обновить категорию (как правило, требуется много времени, чтобы обычно вызывать все обновления продукта), и убедитесь, что каждое задание уникально, включая модуль «Уникальное задание». Уникальное задание использует параметры задания, поэтому, если вы попытаетесь поставить в очередь 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
0 голосов
/ 28 марта 2012

Делайте зависимые обновления в одиночных вызовах 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

Если это слишком медленно, переместите его в задание на восстановление:

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

Делайте аналогичные вещи для других обратных вызовов модели.

...