2012-06-27 5 views
10

Mi versión es:Confusión almacenamiento en caché de consultas de registro activo con Rails.cache.fetch

  • Rieles: 3.2.6
  • Dalli: 2.1.0

Mi env es:

  • config.action_controller.perform_caching = true
  • config.cache_store =: dalli_store, 'loc alhost: 11211' , {: namespace => 'MiNombreEspacio'}

Cuando escribo:

Rails.cache.fetch(key) do 
    User.where('status = 1').limit(1000) 
end 

El modelo de usuario no puede ser almacenado en caché. Si uso

Rails.cache.fetch(key) do 
    User.all 
end 

puede guardarse en caché. ¿Cómo guardar el resultado de la consulta?

Respuesta

30

La razón es porque

User.where('status = 1').limit(1000) 

devuelve un ActiveRecord::Relation que en realidad es tan hacer frente, no una consulta. Rails almacena en caché el alcance.

Si desea almacenar en caché la consulta, debe utilizar un método de consulta al final, como #all.

Rails.cache.fetch(key) do 
    User.where('status = 1').limit(1000).all 
end 

Tenga en cuenta que nunca es una buena idea para almacenar en caché los objetos ActiveRecord. El almacenamiento en caché de un objeto puede provocar estados y valores incoherentes. Siempre debe almacenar en caché objetos primitivos, cuando corresponda. En este caso, considere almacenar en caché los ids.

ids = Rails.cache.fetch(key) do 
    User.where('status = 1').limit(1000).pluck(:id) 
end 
User.find(ids) 

Se puede argumentar que en este caso, una llamada a User.find que siempre ha ejecutado. Es cierto, pero la consulta que utiliza la clave principal es rápida y soluciona el problema que describí anteriormente.Además, el almacenamiento en caché de objetos de registro activos puede ser costoso y puede terminar llenando rápidamente toda la memoria de Memcached con solo una entrada de caché. Los ID de almacenamiento en caché también evitarán este problema.

+0

thx. Si tengo un modelo de categoría, puede tener menos de 100 registros, guardo en caché la Categoría. ¿Es una buena idea? no almacenar en caché la identificación de cateogria – tinylian

+0

¿O primero cambio Category.all a json y los guardo en caché en memcached? – tinylian

+1

Este es un problema muy importante y sutil; nos dimos cuenta de que este problema se había infiltrado en nuestra aplicación: la diferencia entre 'Foo.where (bar: 'fubar')' y 'Foo.where (bar: 'fubar'). all' es completamente diferente w/r/t caché, y algo que puede deslizarse fácil y sutilmente en su código base. Verifique y verifique regularmente si confía en el almacenamiento en caché. –

0

Usando

User.where("status = 1").limit(1000).all 

debería funcionar.

2

Rails.cache.fetch almacena en caché exactamente lo que evalúa el bloque.

 User.where('status = 1').limit(1000) 

es apenas un alcance, por lo que lo que se acaba de caché es el objeto ActiveRecord :: Relation, es decir, la consulta, pero no sus resultados (porque la consulta no se ha ejecutado todavía).

Si quieres algo útil que se almacena en caché, es necesario forzar la ejecución de la consulta dentro del bloque, por ejemplo haciendo

User.where('status = 1').limit(1000).all 

Tenga en cuenta que en los carriles 4, all no significa fuerza de carga de la relación - to_a utilizar en lugar

3

Además de la respuesta seleccionada: para Rails 4+ debe usar load en lugar de all para obtener el resultado del alcance.

+0

'User.find (1) .load' me da' ArgumentError: número de argumentos incorrectos (dado 0, esperado 1..2) 'en Rails 4.1.15 – Kelseydh

Cuestiones relacionadas