Reputation: 195
The information in my header/footer is derived from a bunch of dictionary tables in my database, and the data is going to change extremely infrequently. I would think this is the perfect opportunity for caching, so that every page won't touch the database as far as rendering the header/footer is concerned.
The Rails guide gives an example of fragment caching so I'm thinking for my nav links would be something like...
<% @categories.each do |category| %>
<% cache category do %>
<li class="nav-link">
<%= link_to category.name, category_path(category) %>
</li>
<% end %>
<% end %>
But I don't understand how this prevents contact with the database or optimizes anything. The controller is making the call for @categories before the view renders which means the SQL query is made every page request... some HTML is being cached, but is the rendering process really a significant saving even for a larger snippet?? I feel like the strain on the database is what you really want to be limiting especially if dealing with a lot of concurrent traffic.
What is the appropriate caching strategy for things like footer / navigation partials.?
Upvotes: 2
Views: 71
Reputation: 15848
The Rails Guides example for fragment caching refers only to cache the view generated to show an object, but not the query, that's why you can't see where it caches the query, because it doesn't.
You can use low-level caching to cache the @categories
query https://guides.rubyonrails.org/caching_with_rails.html#low-level-caching
It can cache any kind of information, so, you could have something like
class Category < ActiveRecord::Base
def self.for_navbar
Rails.cache.fetch("#{nav_cache_key}/categories_for_navbar", expires_in: 1.week) do
self.whatever_scope_you_need
end
end
end
What you will need to change is the nav_cache_key
var that's used to identify the cache. I'm not sure about a best practice with this, but I would set a class variable the first time you need it with the current time_stamp and update it whenever the cache should be wiped.
Something like
def self.navbar_cache_key
@@navbar_cache_key ||= Time.now
end
after_update :change_cache_key
def change_cache_key
@@navbar_cache_key = Time.now
end
That way, every time a category is updated, it will change the @@navbar_cache_key
for the class and the cache will be updated for the new key. I'm not sure what are the real conditions where the cache need to be updated, maybe the after_update
callback is not the best or you need some extra actions.
This will cache only the query (not sure if you need to cache an array or caching the query works the same, maye you need a .to_a
at the end), you can still use fragment caching if you want to cache the li
elements too.
I suppose you could even cache the full html of the categories, since low-level cache accepts any kind of info, you only need to find the right place to use Rails.cache.fetch
and the right place to save/update the cache key.
Upvotes: 3