Caché de confusiones de consultas de Active Record con Rails.cache.fetch

Mi versión es:

  • Rieles: 3.2.6
  • dalli: 2.1.0

Mi env es:

  • config.action_controller.perform_caching = true
  • config.cache_store =: dalli_store, ‘localhost: 11211’, {: namespace => ‘MyNameSpace’}

Cuando yo escribo:

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

El modelo de usuario no se puede almacenar en caché. Si uso

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

puede ser almacenado en caché ¿Cómo guardar el resultado de la consulta?

La razón es porque

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

devuelve una relación ActiveRecord::Relation que en realidad es un scope, no una consulta. Rails almacena en caché el scope.

Si desea almacenar en caché la consulta, debe usar 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 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) 

Puede argumentar que en este caso una llamada a User.find siempre se ejecuta. 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.

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

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

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

Es solo un ámbito, por lo que lo que se almacena en caché es simplemente el objeto ActiveRecord :: Relation, es decir, la consulta, pero no sus resultados (porque la consulta no se ha ejecutado todavía).

Si desea almacenar en caché algo útil, debe 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 Rails 4, all no fuerza la carga de la relación – use to_a en to_a lugar

Utilizando

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

Deberia trabajar.