2012-02-16 14 views
6

Sto importando i record in blocco e non voglio aggiornare il contatore ogni volta. Voglio saltare gli aggiornamenti sql counter_cache durante il bulk upsert, quindi chiamare reset_counters alla fine del ciclo.Come disabilitare l'associazione Rails counter_cache al runtime

ho provato:

my_model = MyModel.find_or_initialize_by_slug row[:slug] 
my_model.association(:my_association).reflection.options[:counter_cache] = false 
my_model.attributes = {:name => "Christopher"} 
my_model.save! 

ma posso vedere nell'output SQL che è ancora l'aggiornamento del counter_cache.

nota: non posso usare ActiveRecord-import perché voglio eseguire upserts e sto usando PostgreSQL

+0

Hai risolto questo? Stesso problema... –

risposta

5

Avete alcune opzioni diverse per saltare un aggiornamento della cache contatore, e quello che si sceglie dipende davvero da come vuoi strutturare la tua applicazione. Discuterò i diversi modi in cui è possibile aggirare la cache del contatore e menzionare alcune delle considerazioni che si potrebbero voler fare nel farlo.

Fondamentalmente, ci sono tre diversi modi si può saltare l'aggiornamento della cache contatore:

  1. Monkey patch your model to disable the counter cache callback
  2. Use an update method that doesn't trigger callbacks
  3. definire un modello alternativo che punta alla stessa tabella che non ha la stesso comportamento di callback e usarlo quando si inserisce massa nel database

Si noti che le prime due opzioni sopra sono più g enerally related to disabilitazione di callback in ActiveRecord, e questo ha senso perché la counter cache è implementata internamente per mezzo di un callback.

Quando Rails carica un modello che ha associazioni con una cache contatore, definisce dinamicamente i metodi di callback. Se si desidera disabilitare questi come callback, è necessario prima capire quali sono i nomi di callback.

Ci sono due modi principali per capire quali sono i metodi che Rails ha definito per implementare questi callback. Puoi leggere l'origine di Rails per scoprire i nomi che genererà tramite l'intorpolazione String, oppure puoi usare l'introspezione per capire a quali metodi risponde la tua classe. Fornirò un esempio di come è possibile utilizzare l'introspezione per scoprire i callback definiti da ActiveRecord al fine di implementare automaticamente la cache di contatori.

Supponiamo di avere una classe chiamata SpecialReply che discende da una classe Rispondi che discende da ActiveRecord :: Base (this example comes from the test suite with Rails). Ha una colonna della cache contatore come definito di seguito:

class SpecialReply < ::Reply 
    belongs_to :special_topic, :foreign_key => 'parent_id', :counter_cache => 'replies_count' 
end 

Nella console, è possibile vedere quali metodi la classe risponde utilizzando .methods. Questo sta per produrre un sacco di rumore, dal momento che ogni istanza di Object risponde già ad un sacco di metodi, in modo da poter restringere la lista come segue:

1.9.3-p194 :001 > sr = SpecialReply.new 
1.9.3-p194 :002 > sr.methods - Object.methods 

Nella seconda riga che stai dicendo, spettacolo tutti i metodi a cui risponde la mia istanza di SpecialReply, meno quelli a cui tutti gli oggetti rispondono. Questo spesso aiuta con l'introspezione filtrando i metodi che non sono particolari per il tipo di classe che stai guardando.

Sfortunatamente anche dopo questo filtraggio c'è molto rumore dovuto ai metodi che ActiveRecord aggiunge a tutte le sue classi discendenti. In questo caso grep è utile - dal ActiveRecord crea utilmente metodi di callback da banco che contengono la stringa counter_cache (see the meta-programming used by ActiveRecord to generate a counter cache method for a belongs_to association), è possibile scoprire i callback definiti relativi a contrastare le cache con il seguente:

1.9.3-p194 :001 > sr = SpecialReply.new 
1.9.3-p194 :002 > sr.methods.map(&:to_s).grep(/counter_cache/) 

Si noti che dal grep opera su una stringa e methods restituisce una matrice di nomi di metodi simbolo, per prima cosa utilizziamo uno to_proc (&:) per convertire tutti i simboli in stringhe e quindi grep out quelli contenenti counter_cache. Questo mi lascia con i seguenti metodi che sembrano come se fossero probabilmente generato automaticamente da ActiveRecord come callback per l'attuazione contatore caching:

belongs_to_counter_cache_after_create_for_special_topic 
belongs_to_counter_cache_before_destroy_for_special_topic 
belongs_to_counter_cache_after_create_for_topic 
belongs_to_counter_cache_before_destroy_for_topic 
belongs_to_counter_cache_after_create_for_topic_with_primary_key 
belongs_to_counter_cache_before_destroy_for_topic_with_primary_key 

si dovrebbe essere in grado di seguire un processo simile nel vostro programma per determinare i nomi dei metodi aggiunti da ActiveRecord in modo da poterli buttare fuori dopo existing instructions for removing callbacks.

La scelta tra le opzioni di cui sopra dipende in realtà dalla struttura del programma e dai compromessi che si desidera prendere in considerazione per una maggiore efficienza dei dati di caricamento. È importante notare che le prime due opzioni possono rendere il codice meno leggibile modificando il comportamento di una classe dall'esterno (patch di scimmia) e possono rendere instabile il sistema ignorando le regole di business (aggiornamenti delle colonne della cache) sugli aggiornamenti dei dati. Per questi motivi, vorrei riflettere sull'opportunità di creare un'altra classe per caricare i dati in modo ottimizzato, riducendo al minimo l'impatto sulla leggibilità o sulla coerenza dei dati.

Problemi correlati