Memorizzazione nella cache di Confusione di query attive con Rails.cache.fetch

La mia versione è:

  • Rotaie: 3.2.6
  • dalli: 2.1.0

Il mio invito è:

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

Quando scrivo:

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

Il modello utente non può essere memorizzato nella cache. Se io uso

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

può essere memorizzato nella cache. Come memorizzare nella cache il risultato della query?

Il motivo è perché

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

restituisce un ActiveRecord::Relation che è in realtà un ambito, non una query. Rails memorizza nella cache l’ambito.

Se si desidera memorizzare nella cache la query, è necessario utilizzare un metodo di query alla fine, ad esempio #all .

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

Si noti che non è mai una buona idea mettere in cache gli oggetti ActiveRecord . La memorizzazione nella cache di un object può causare stati e valori incoerenti. Dovresti sempre memorizzare nella cache gli oggetti primitivi, quando applicabile. In questo caso, considera di memorizzare gli id ​​in cache.

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

Si potrebbe sostenere che in questo caso una chiamata a User.find è sempre eseguita. È vero, ma la query che utilizza la chiave primaria è veloce e si aggira il problema che ho descritto prima. Inoltre, la memorizzazione nella cache degli oggetti record attivi può essere costosa e si potrebbe finire rapidamente per riempire tutta la memoria Memcached con una sola voce della cache. Gli id ​​di cache impediranno anche questo problema.

Oltre alla risposta selezionata: per Rails 4+ dovresti usare load invece di all per ottenere il risultato dell’oscilloscopio.

Rails.cache.fetch memorizza Rails.cache.fetch cache esattamente ciò che il blocco valuta.

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

È solo un ambito, quindi ciò che viene memorizzato nella cache è solo l’object ActiveRecord :: Relation, ovvero la query, ma non i suoi risultati (perché la query non è stata ancora eseguita).

Se vuoi qualcosa di utile da memorizzare nella cache, devi forzare l’esecuzione della query all’interno del blocco, ad esempio eseguendo

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

Nota che sulle rotaie 4, all non forza il caricamento della relazione – usa invece to_a

utilizzando

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

dovrebbe funzionare.