2009-09-23 4 views
10

Sto lavorando a un'app Rails, dove sto usando il caching delle pagine per archiviare l'output html statico. La memorizzazione nella cache funziona correttamente. Sto avendo problemi a sca- dere le cache, però.In Rails, uno Sweeper non viene chiamato in una configurazione solo modello

Credo che il mio problema sia, in parte, perché non sto scadendo la cache dal mio controller. Tutte le azioni necessarie per questo sono gestite all'interno del modello. Sembra che dovrebbe essere fattibile, ma tutti i riferimenti alla scadenza della cache basata su modello che sto trovando sembrano non aggiornati o altrimenti non funzionano.

Nel mio file environment.rb, sto chiamando

config.load_paths += %W(#{RAILS_ROOT}/app/sweepers) 

E ho, nella cartella/spazzatrici, un file LinkSweeper:

class LinkSweeper < ActionController::Caching::Sweeper 
    observe Link 

    def after_update(link) 
    clear_links_cache(link) 
    end 

    def clear_links_cache(link) 
    # expire_page :controller => 'links', :action => 'show', :md5 => link.md5 
    expire_page '/l/'+ link.md5 + '.html' 
    end 
end 

Quindi ... perché isn Eliminando la pagina in cache quando aggiorno il modello? (Processo: utilizzando script/console, sto selezionando gli elementi dal database e li salvo, ma le loro pagine corrispondenti non vengono cancellate dalla cache) e sto anche chiamando il metodo specifico nel modello Link che normalmente invoca lo spazzino. Né funziona.

Se è importante, il file memorizzato nella cache è un hash md5 su un valore chiave nella tabella Collegamenti. La pagina memorizzata nella cache viene memorizzata come qualcosa come /l/45ed4aade64d427...99919cba2bd90f.html.

In sostanza, sembra che lo Sweeper non stia effettivamente osservando il collegamento. Ho anche letto (here) che potrebbe essere possibile aggiungere semplicemente lo sweeper a config.active_record.observers in environment.rb, ma non sembra farlo (e non ero sicuro se il load_path di app/sweepers in environment.rb ha ovviato a ciò).

+0

Ho appena trovato questo post che copre l'argomento con dovizia di particolari: [Rails 3.1 caching: action_caching e cache spazzatrici] (http://tech.thereq.com/post/13375604886/rails-3-1-caching -action-caching-and-cache-sweepers? 1e2bdcc0 # disqus_thread) – dgilperez

risposta

5

Solo una nota: è possibile utilizzare cache_sweeper in ApplicationController.

class ApplicationController < ActionController::Base 
    cache_sweeper :my_sweeper 
end 

class MySweeper < ActionController::Caching::Sweeper 
    observe MyModel 

    def after_update(my_model) 
    expire_page(...) 
    end 
end 
+0

Grazie per questo. Non l'ho provato, ma mi piacerebbe ricevere commenti da chiunque altro abbia provato questo approccio. – charliepark

+0

Ha funzionato per me. –

+1

Questo funziona se modifico MyModel tramite controller (browser). Tuttavia, questo non verrà chiamato se modifico MyModel tramite console, poiché questo ignora il controller, quindi l'after_update non è mai stato registrato sul modello. – lulalala

0

Sono stato in grado di farlo funzionare, per modo di aggiungere

ActionController::Base.expire_page(app.link_path(:md5 => @link.md5)) 

al metodo nel Modello stesso che è l'aggiornamento del database. Tuttavia, questo sembra un po 'hacky, e mi piacerebbe sapere se qualcuno può spiegare perché non funziona con la normale configurazione della spazzatrice e se c'è un modo più elegante per gestirlo.

Questo snippet di codice (a parte le personalizzazioni che ho inserito per la mia app) è venuto da this post on ruby-forum.com.

11

Quindi ho provato diversi approcci diversi, per vedere cosa funziona e cosa no.

Anche in questo caso, per riassumere la situazione: Il mio obiettivo è quello di scadere pagine memorizzate nella cache quando un oggetto gli aggiornamenti, ma per scadere loro senza fare affidamento su un'azione di controllo. Le spazzatrici convenzionali usano una linea nel controller per notificare alla spazzatrice che deve funzionare. In questo caso, non posso usare una linea nel controller, poiché l'aggiornamento sta avvenendo all'interno del modello. Le normali esercitazioni di sweeper non funzionano, poiché presumono che l'interazione principale con l'oggetto del database avvenga tramite il controller.

Se, leggendo questo, si vede un modo per stringere il mio codice, si prega di commentare e fammi sapere.

In primo luogo, diamo un'occhiata alle cose che funzionano, nel caso in cui anche tu sia bloccato su questo, e hai bisogno di aiuto.

Di tutte le cose che ho provato, l'unica cosa che davvero sembrava funzionare era dichiarare un comando after_update nell'Observer per il modello. In quel comando, ho usato il comando esplicito per l'azione expire_page e incluso un percorso che era stato dichiarato in routes.rb.

Così. Questo funziona:

in config/routes.rb:

map.link 'l/:md5.:format', :controller => 'links', :action => 'show' 

in app/modelli/link_observer.rb:

def after_update(link) 
    ActionController::Base.expire_page(app.link_path(:md5 => link.md5)) 
end 

Si noti che che "MD5" è specifico per la mia app. Potresti voler usare: id o qualche altro identificatore univoco.

Ho anche trovato che dichiara che la linea ActionController :: Base ... dal metodo nel modello che sta facendo l'aggiornamento ha funzionato. Cioè, all'interno di Link.rb, nel metodo che sta effettivamente aggiornando il database, se ho appena bloccato l'intera riga, ha funzionato. Ma dato che potrei voler far scadere la cache della pagina su altri metodi in futuro, preferirei che fosse estratta nell'Observer.

Ora, diamo un'occhiata ad alcune cose che NON FUNZIONANO, nel caso tu stia cercando su Google per questo.

Calling "expire_page (...)" all'interno del metodo after_update (link) all'interno di link_observer.rb non ha funzionato, in quanto ha restituito un "metodo non definito` expire_page '" errore

Creazione di una spazzatrice il file che osservava che il Modello non funzionava. Non sono riuscito a trovare alcun codice di errore, ma sembrava non essere nemmeno consapevole del fatto che aveva un lavoro da fare. Questo era dopo aver chiamato esplicitamente "config.load_paths + =% W (# {RAILS_ROOT}/app/sweepers)" in environment.rb. Solo nel caso in cui qualcosa di grasso dalle dita in quel codice, qui è:

class LinkSweeper < ActionController::Caching::Sweeper 
    observe Link 

    def after_update(link) 
    clear_links_cache(link) 
    end 

    def clear_links_cache(link) 
    # DID NOT WORK expire_page :controller => 'links', :action => 'show', :md5 => link.md5 
    # DID NOT WORK expire_page '/l/'+ link.md5 + '.html' 
    # DID NOT WORK ActionController::Base.expire_page(app.link_path(:md5 => link.md5)) 
    end 
end 

che al di sopra esempio aveva il file link_sweeper.rb in una directory,/app/spazzatrici. Ho anche provato a mettere link_sweeper.rb all'interno della directory app/modelli, e provato a chiamare con il comando config.active_record.observers in environment.rb:

config.active_record.observers = :link_observer, :link_sweeper 

Ma che non ha funzionato, neanche.

Quindi, sì. È abbastanza probabile che uno di questi metodi funzioni e che abbia incasinato qualcosa nel codice. Ma penso di aver fatto tutto dal libro.

In definitiva, per riassumere: Anziché utilizzare una Sweeper per far scadere il caching delle pagine, si desidera impostare un after_ callback nell'Observer del modello. Ti consigliamo di utilizzare il percorso esplicito al metodo Base.expire_page:

def after_update(<model>) # where <model> is the name of the model you're observing 
    ActionController::Base.expire_page(app.<model>_path(:id => <model>.id)) # where <model> is the name of the model you're observing 
end 

Speriamo che questo vi aiuterà a qualcun altro lungo la strada. Di nuovo, se vedi qualcosa nel mio codice non funzionante dove avrei dovuto fare qualcosa di diverso, per favore fammi sapere. Se vedi qualcosa nel mio codice di lavoro che può essere più stretto, ti prego di farmelo sapere anch'io.

+2

Quindi tutto ciò che avevo sopra questo funzionava sul mio server di sviluppo. Quando l'ho provato sul server di produzione, tuttavia, si è rotto. Ho provato un certo numero di approcci alternativi, ma nessuno di loro ha funzionato davvero. Alla fine, quindi, ho riscritto quella parte dell'app, quindi esegue la scadenza della pagina da un controller. Sembra che funzioni per ora. – charliepark

2

Ho ottenuto questo funzionamento. L'unica leggera differenza nel mio setup è che lo sweeper fa parte di un motore Rails; che tiene conto di piccole differenze (caricando il file sweeper con un require nel init del motore invece di aggiungerlo al percorso di caricamento in environment.rb, ecc.).

Così, la spazzatrice viene caricato nel init.rb del motore come questo:

require File.join(File.dirname(__FILE__), 'app', 'sweepers', cached_category_count_sweeper') 

ho chiamato una spazzatrice perché "spazza" la cache, ma credo che il suo solo un osservatore sulla modello:

class CachedCategoryCountSweeper < ActiveRecord::Observer 
    observe CategoryFeature 

    def before_save(cf) 
    expire_cache(cf.category_id_was) if cf.category_id_changed? 
    end 

    def after_save(cf) 
    expire_cache(cf.category_id) 
    end 

    def after_destroy(cf) 
    expire_cache(cf.category_id) 
    end 

    def expire_cache(c) 
    ApplicationController.expire_page("/categories/#{c}/counts.xml") if !c.nil? 
    end 
end 

Francamente, non mi piace dover codificare il percorso, ma ho provato ad aggiungere:

include ActionController:UrlWriter 

e quindi utilizzando il metodo del percorso, ma ha funzionato solo per me in fase di sviluppo. Non ha funzionato in produzione, perché il mio server di produzione utilizza una radice relativa url (invece di host virtuali) e il metodo interno "page_cache_path" otterrebbe il percorso del file in modo coerente in modo che non potesse scadere.

Poiché si tratta di un osservatore, ho aggiunto al environment.rb:

config.active_record.observers = :cached_category_count_sweeper 

Infine, il controller che utilizza la cache (Non scade esso, che è fatto attraverso il modello osservatore):

class CachedCategoryCountsController < ApplicationController 
    caches_page :index 

    # GET /cached_category_counts.xml 
    def index 
    ... 
    end 
end 

In ogni caso, spero che questo aiuti.

Andres Montano

+0

Grazie per aver lavorato su questo problema. Non ho provato il codice, ma sembra che tu abbia fatto un accurato lavoro di test. Se qualcun altro lo prova e ha un feedback, per favore lasciatelo qui. Mi piacerebbe sapere come funziona (positivamente o negativamente) per gli altri. – charliepark

+0

Come hai creato il file init.rb e instradato nell'app Rails? –

0

ho scritto un po 'su questo argomento qui: Rails Cache Sweeper Confusion. Mi piacerebbe sentire le tue opinioni.

+1

Hai appena sollevato la domanda sul perché non è come avresti voluto che fosse. Il post del blog potrebbe aiutare di più se: backlinks a questo thread, spiega il problema con esempi, fornisce una panoramica del nucleo di rotaie – Overbryd

+1

Il link non è più valido. – traday

3

Si è verificato lo stesso problema durante il tentativo di eseguire il caching dei frammenti (rails 3). Impossibile far sì che lo sweeper osservasse, così ho optato per la soluzione per renderlo un osservatore AR come descritto sopra e chiamando lo ApplicationController.new.expire_fragment(...).

0

In base alle risposte di @moiristo e @ZoogieZork, suppongo che questo funzionerebbe (non verificato).

class LinkSweeper < ActiveRecord::Observer 
    include ActionController::Caching::Pages 
    # or if you want to expire fragments 
    #include ActionController::Caching::Fragments 

    observe Link 

    def after_update(link) 
    expire_page(...) 
    #expire_fragment(...) 
    end 
end 
Problemi correlati