Introduction
Russian doll caching nests cached fragments inside each other, so when an inner fragment changes, only that fragment and its direct parents are invalidated — siblings remain cached. This dramatically reduces cache misses.
Key Concepts
- Nested Caching: Cache fragments contain other cache fragments, forming a hierarchy.
- Touch Propagation: When a child record updates,
touch: truepropagates the timestamp change up the chain. - Cache Digest: Rails includes a template digest in cache keys, so template changes automatically invalidate caches.
Real World Context
An e-commerce category page with 100 products, each showing reviews: when one review is added, only that product's cache invalidates. The other 99 products serve from cache instantly.
Deep Dive
The Pattern
erb<% cache ['posts', @posts.maximum(:updated_at)] do %> <% @posts.each do |post| %> <% cache post do %> <article> <h2><%= post.title %></h2> <% cache [post, 'comments'] do %> <div class="comments"> <% post.comments.each do |comment| %> <% cache comment do %> <p><%= comment.body %></p> <% end %> <% end %> </div> <% end %> </article> <% end %> <% end %> <% end %>
Setting Up Touch Propagation
rubyclass Comment < ApplicationRecord belongs_to :post, touch: true end class Post < ApplicationRecord belongs_to :author, touch: true has_many :comments, dependent: :destroy end
When a comment changes:
- Comment's cache invalidates
- Post's
updated_atupdates viatouch: true - Post's cache invalidates
- Sibling posts remain cached
Template Digest Keys
Rails automatically includes a digest of the template in cache keys:
erb<% cache post do %> <%= post.title %> <!-- Change this template, cache auto-refreshes --> <% end %>
Common Pitfalls
- Missing touch declarations — If you forget
touch: trueon the child association, parent caches won't invalidate when children change. - Too many nesting levels — More than 3-4 levels of nesting adds complexity without much benefit. Keep it practical.
Best Practices
- Always add touch: true on belongs_to — Any association whose parent is cached should propagate timestamp changes.
- Use maximum(:updated_at) for collection keys — This ensures the outer cache invalidates when any item in the collection changes.
Summary
- Russian doll caching nests fragments so siblings stay cached when one changes.
touch: truepropagates timestamp changes up the association chain.- Rails auto-includes template digests in cache keys.
- Keep nesting to 3-4 levels maximum for maintainability.
Code Examples
ruby
# Touch propagation setup
class Comment < ApplicationRecord
belongs_to :post, touch: true
end
class Post < ApplicationRecord
belongs_to :category, touch: true
has_many :comments
end
# When a comment is saved:
# 1. comment.updated_at changes
# 2. post.updated_at changes (touch)
# 3. category.updated_at changes (touch)
# All related caches invalidate automatically