Запутывание кэширования запросов Active Record с помощью Rails.cache.fetch
Моя версия:
- Rails: 3.2.6
- dalli: 2.1.0
Моя env:
- config.action_controller.perform_caching = true
- config.cache_store =: dalli_store, 'localhost: 11211', {: namespace = > 'MyNameSpace'}
Когда я пишу:
Rails.cache.fetch(key) do
User.where('status = 1').limit(1000)
end
Пользовательскую модель нельзя кэшировать. Если я использую
Rails.cache.fetch(key) do
User.all
end
он может быть кэширован. Как кэшировать результат запроса?
Ответы
Ответ 1
Причина в том, что
User.where('status = 1').limit(1000)
возвращает ActiveRecord::Relation
, который на самом деле является областью, а не запросом. Рельсы кэшируют область действия.
Если вы хотите кэшировать запрос, вам нужно использовать метод запроса в конце, например #all
.
Rails.cache.fetch(key) do
User.where('status = 1').limit(1000).all
end
Обратите внимание, что никогда не рекомендуется кэшировать объекты ActiveRecord. Кэширование объекта может привести к несогласованным состояниям и значениям. Вы всегда должны кэшировать примитивные объекты, когда это применимо. В этом случае рассмотрим кэширование идентификаторов.
ids = Rails.cache.fetch(key) do
User.where('status = 1').limit(1000).pluck(:id)
end
User.find(ids)
Вы можете утверждать, что в этом случае вызов User.find
всегда выполняется. Это правда, но запрос с использованием первичного ключа выполняется быстро, и вы обошли проблему, о которой я говорил ранее. Более того, кэширование активных объектов записи может быть дорогостоящим, и вы можете быстро завершить заполнение всей памяти Memcached только одной записью одного кэша. Кэширование ids также предотвратит эту проблему.
Ответ 2
В дополнение к выбранному ответу: для Rails 4+ вы должны использовать load
вместо all
для получения результата области.
Ответ 3
Rails.cache.fetch
кэширует то, что блок оценивает.
User.where('status = 1').limit(1000)
Является просто областью, поэтому кэширование - это только объект ActiveRecord:: Relation, то есть запрос, но не его результаты (потому что запрос еще не выполнен).
Если вам нужно что-то полезное для кэширования, вам нужно принудительно выполнить выполнение запроса внутри блока, например, выполнив
User.where('status = 1').limit(1000).all
Обратите внимание, что на рельсах 4 all
не принудительно загружает отношение - используйте to_a
вместо
Ответ 4
Использование
User.where("status = 1").limit(1000).all
должен работать.