2012-03-12 3 views
5

Per tutti i seguenti assumere questi:calcoli attributo Conseguenti con un sistema di accodamento

  • rotaie v3.0
  • rubino v1.9
  • resque

Abbiamo 3 modelli:

  • Prodotto belongs_to: SKU, belongs_to: categoria
  • Cod has_many: prodotti, belongs_to: categoria
  • Categoria has_many: prodotti, has_many: SKU

Quando abbiamo aggiornare il prodotto (diciamo abbiamo disabilitato it) abbiamo bisogno di avere alcune cose che accadono allo sku e alla categoria rilevanti. Lo stesso vale per quando uno sku viene aggiornato.

Il corretta modo per raggiungere questo è avere un after_save su ogni modello che fa scattare gli eventi di aggiornamento degli altri modelli.

esempio:

products.each(&:disable!) 
# after_save triggers self.sku.products_updated 
# and self.category.products_updated (self is product) 

Ora, se abbiamo 5000 prodotti che sono dentro per un ossequio. La stessa categoria potrebbe essere aggiornata centinaia di volte e incasinare il database mentre lo fa.

Abbiamo anche un buon sistema di accodamento, quindi il modo più aggiornato di aggiornare i prodotti sarebbe products.each(&:queue_disable!), che dovrebbe semplicemente aggiungere 5000 nuove attività alla coda di lavoro. Il problema di 5000 aggiornamenti di categoria esiste comunque.

C'è un modo per evitare tutti quegli aggiornamenti sul db?

Come possiamo concatenare tutti i category.products_updated per ogni categoria nella coda?

+0

Perché è necessario aggiornare una categoria quando il prodotto viene modificato? È un contatore un prezzo medio o cosa? In tal caso, rilascia la cache del contatore e calcolala quando è necessaria oppure usa http://redis.io/. –

risposta

0

Effettua gli aggiornamenti dipendenti in singole chiamate SQL. #update_all aggiornerà molti record contemporaneamente. Ad esempio,

In un callback after_update, aggiornare tutti i valori della colonna dipendenti:

class Category 
    after_update :update_dependent_products 

    def update_dependent_products 
    products.update_all(disabled: disabled?) if disabled_changed? 
    end 
end 

Se questo è troppo lento, spostarlo in un lavoro 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 

fare cose simili per il altri callback del modello.

+0

Quello che sto cercando di fare è il contrario: aggiorno un po 'di prodotti e ogni aggiornamento deve propagarsi alla categoria, ma sarà meglio se tutti gli aggiornamenti del prodotto fossero stati fatti prima e poi solo un aggiornamento per ogni categoria interessata . – Kostas

+0

OK ho frainteso quello che stavi chiedendo. Ho aggiunto un'altra risposta e lascerò questo. – tee

2

È possibile garantire un aggiornamento di categoria singola per tutti i prodotti utilizzando un paio di plug-in Resque: Resque Unique Job e Resque Scheduler.

Ritardare l'esecuzione del lavoro per aggiornare leggermente la categoria (anche se in genere richiede tutti gli aggiornamenti del prodotto) e garantire che ogni lavoro sia univoco includendo il modulo Lavoro univoco. Un lavoro univoco utilizza i parametri del lavoro, quindi se provi a mettere in coda 2 lavori con category_id 123, ignora il 2 ° poiché il lavoro è già in coda.

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