Search code examples
ruby-on-rails-3russian-doll-caching

Rails 4.0.0.0 Russian Doll Caching with belongs_to model


I have set up a cache in my model like

def self.latest(shop_id)
    Inventory.where(:shop_id => shop_id).order(:updated_at).last
end

and in my view

<% cache ['inventories', Inventory.latest(session[:shop_id])] do %>

   <% @inventories.each do |inventory| %>

      <% cache ['entry', inventory] do %>     

     <li><%= link_to inventory.item_name, inventory %></li>

So, here I can have many shops, each with an inventory of stock items. Will the above cache work at all for different shops?

I think it's possible that even displaying the view in a different shop will break the cache. Or, any shop adding an inventory item will break the cache.

Can I use Russian Doll caching like this or do I need to use Inventory.all in my model?


Solution

  • Your idea is close, but you need to include the shop_id, the count, and the maximum updated_at of each shop's inventory into your cache key. Your outer cache needs to get busted when a shop's item gets deleted too, and that isn't covered under a max id or updated_at alone.

    You can expand your custom cache key helper method to make this work. This allows you to create unique top level caches that only get busted when a member of that set gets added, updated or deleted. In effect, this gives a unique outer cache for each shop_id. Thus when one shop's inventory is changed, it doesn't affect another shop's cache.

    Here is an example, based on ideas in the edge rails documentation:

    module InventoriesHelper
      def cache_key_for_inventories(shop_id)
        count          = Inventory.where(:shop_id => shop_id).count
        max_updated_at = Inventory.where(:shop_id => shop_id).maximum(:updated_at).try(:utc).try(:to_s, :number)
        "inventories/#{shop_id}-#{count}-#{max_updated_at}"
      end
    end
    

    Then in your view:

    <% cache(cache_key_for_inventories(session[:shop_id])) do %>
      ...
    <% end %>