问题
I am trying to setup Model and Fragment Caching in Rails 5.2.1
I have had success with Fragment caching, but I am still seeing database queries after implementing model caching for my Model.
I have enabled development caching
$ rails dev:cache
Model Helper
module LanguagesHelper
def approved_languages
Rails.cache.fetch("approved_languages") { Languages.is_active.is_approved }
end
end
Controller
class LanguagesController < ApplicationController
include LanguagesHelper
def index
@languages = approved_languages
end
end
Views
app/views/languages/index.html.erb
<%= render partial: 'languages/language', collection: @languages, cached: true %>
app/views/languages/_language.html.erb
<% cache language do %>
<%= language.name %>
<% end %>
Console
Started GET "/languages" for 127.0.0.1 at 2018-08-21 14:13:29 -0400
Processing by LanguagesController#index as HTML
Rendering languages/index.html.erb within layouts/application
Language Load (1.2ms) SELECT "languages".* FROM "languages" WHERE "languages"."deleted" = $1 AND "languages"."approved" = $2 [["deleted", false], ["approved", true]]
↳ app/views/languages/index.html.erb:4
Rendered collection of languages/_language.html.erb [1 / 1 cache hits] (3.0ms)
Rendered languages/index.html.erb within layouts/application (10.9ms)
Completed 200 OK in 50ms (Views: 46.2ms | ActiveRecord: 1.2ms)
Why am I still seeing database queries with each request?
回答1:
What seems to be going on here is that you're caching the relation before it loads the records, and so it still has to actually load them before it can be used (using my Ad model because it's there and convenient for testing in irb):
ads = Ad.all;nil # no query here, this is what I think you're caching
# this next line is where the query is run, (this would be
# equivalent to your render line)
ads.each { ... }
# Ad Load (0.1ms) SELECT "ads".* FROM "ads"
instead you might try forcing active record to load the relation before you cache it and see if that helps. You can do this by using load:
ads = Ad.all.load;nil # query is now run here
# Ad Load (0.1ms) SELECT "ads".* FROM "ads"
ads.each { ... } # and not run here
and together with caching enabled (all in a single rails console session, multiple sessions seems to forget the cache from the previous, but I've not configured caching so probably just an in-memory store of some kind)
ads = Rails.cache.fetch("test load") { Ad.all.load };nil # query
# Ad Load (0.9ms) SELECT "ads".* FROM "ads"
ads = Rails.cache.fetch("test load") { Ad.all.load };nil # no query
ads.each { } # no query
ads = Rails.cache.fetch("test without load") { Ad.all };nil # no query
ads.each { };nil # query
# Ad Load (0.1ms) SELECT "ads".* FROM "ads"
ads = Rails.cache.fetch("test without load") { Ad.all };nil # no query
ads.each { };nil # query
# Ad Load (0.1ms) SELECT "ads".* FROM "ads"
回答2:
I was close before but now have successfully implemented caching. The above answer was appreciated but not what I was looking for, this is my current setup.
I was calling the Language model query in a Helper module when I should have moved it to the model.
Model
after_save :clear_cache
after_destroy :clear_cache
def clear_language_cache
Rails.cache.delete('Language.active.approved')
end
def self.active_approved
Rails.cache.fetch('Language.active.approved') { is_active.is_approved.order(created_at: :desc).to_a }
end
Controller
class LanguagesController < ApplicationController
def index
@languages = Language.active_approved
end
end
Console
Started GET "/languages" for 127.0.0.1 at 2018-08-22 18:13:21 -0400
Processing by LanguagesController#index as HTML
Rendering languages/index.html.erb within layouts/application
Rendered collection of languages/_language.html.erb [1 / 1 cache hits] (11.0ms)
Rendered languages/index.html.erb within layouts/application (15.9ms)
Completed 200 OK in 68ms (Views: 45.5ms | ActiveRecord: 5.8ms)
来源:https://stackoverflow.com/questions/51954529/rails-5-2-1-model-fragment-caching