2012-03-12 3 views
5

Для всех следующих предположим, они:Последовательные вычисления атрибутов с системой массового обслуживания

  • рельсы v3.0
  • рубин v1.9
  • спасательное

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

  • Продукт belongs_to: Код, BELONGS_TO: категория
  • Sku has_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 категорий все еще существует.

Есть ли способ избежать всех этих обновлений на db?

Как мы можем объединить все категории.products_updated для каждой категории в очереди?

+0

Зачем вам нужно обновлять категорию при изменении продукта? Это счетчик средней цены или что? Если это так, отбросьте кеш-счетчик и вычислите его, когда это необходимо, или используйте http://redis.io/. –

ответ

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 

сделать то же самое для другие обратные вызовы модели.

+0

То, что я пытаюсь сделать, идет наоборот: я обновляю кучу продуктов, и каждое обновление должно распространяться в категорию, но будет лучше, если бы все обновления продукта были выполнены в первую очередь, а затем только одно обновление для каждой затронутой категории , – Kostas

+0

OK Я неправильно понял, что вы просили. Я добавил еще один ответ и оставил это. – tee

2

Вы можете обеспечить обновление одной категории для всех продуктов, используя пару плагинов 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 
Смежные вопросы