Skip to content

Ror Caching

DodaTech 4 min read

title: Ruby on Rails Caching — Complete Guide to Performance Optimization description: 'Learn Ruby on Rails caching: page caching, fragment caching, Russian doll caching, low-level caching with Redis, HTTP caching, and Turbo Streams caching.' date: 2026-06-28 lastmod: 2026-06-28 weight: 30 tags: [backend, ror]


Rails caching stores fragments of views, whole pages, or query results to reduce database load and dramatically improve response times for repeated requests.

## What You'll Learn

By the end of this tutorial, you'll implement fragment caching, Russian doll caching, low-level Redis caching, HTTP caching with ETags, use Turbo Streams caching, and measure cache effectiveness.

## Real-World Use

A blog site caches post listings (fragment caching), individual posts (Russian doll), and sidebar widgets. First request generates the cache, subsequent requests serve cached content in milliseconds.

## Caching Learning Path

```mermaid
flowchart LR
  A[Background Jobs] --> B[Caching]
  B --> C[Security]
  C --> D[Docker]
  D --> E[Deployment]
  B --> F{You Are Here}
  style F fill:#f90,color:#fff

Fragment Caching

<!-- app/views/posts/index.html.erb -->
<h1>Posts</h1>
<% @posts.each do |post| %>
  <% cache post do %>
    <article>
      <h2><%= link_to post.title, post %></h2>
      <p><%= truncate(post.body, length: 200) %></p>
      <small><%= time_ago_in_words(post.created_at) %> ago</small>
    </article>
  <% end %>
<% end %>
<%= cache("posts-count") do %>
  <p>Total: <%= Post.published.count %> posts</p>
<% end %>

Russian Doll Caching

<!-- app/views/posts/show.html.erb -->
<% cache @post do %>
  <h1><%= @post.title %></h1>
  <p><%= @post.body %></p>
  <div class="comments">
    <%= render @post.comments %>  <!-- Each comment cached separately -->
  </div>
<% end %>

<!-- app/views/comments/_comment.html.erb -->
<% cache comment do %>
  <div class="comment">
    <p><strong><%= comment.user.name %></strong> said:</p>
    <p><%= comment.body %></p>
    <small><%= time_ago_in_words(comment.created_at) %> ago</small>
  </div>
<% end %>

Low-Level Caching

# app/models/post.rb
class Post < ApplicationRecord
  def self.recent_published
    Rails.cache.fetch("posts/recent_published", expires_in: 5.minutes) do
      published.recent.limit(10).includes(:user).to_a
    end
  end

  def self.category_counts
    Rails.cache.fetch("posts/category_counts", expires_in: 1.hour) do
      group(:category).count
    end
  end
end

# In controller
def index
  @posts = Rails.cache.fetch("posts/page_#{params[:page]}", expires_in: 1.minute) do
    Post.recent_published
  end
end

Redis Cache Store

# config/environments/production.rb
config.cache_store = :redis_cache_store, {
  url: ENV.fetch("REDIS_URL", "redis://localhost:6379/1"),
  expires_in: 1.hour,
  namespace: "myapp:cache",
  pool_size: 5
}
# Manual cache operations
Rails.cache.write("user_#{user.id}_stats", stats, expires_in: 30.minutes)
stats = Rails.cache.read("user_#{user.id}_stats")
Rails.cache.delete("stale_cache_key")
Rails.cache.clear  # Use cautiously in production
Rails.cache.fetch("key", expires_in: 1.hour) { expensive_operation }

HTTP Caching

class PostsController < ApplicationController
  # ETag + Last-Modified
  def show
    @post = Post.find(params[:id])
    fresh_when(@post)  # Sets ETag and Last-Modified
    # Returns 304 Not Modified if client has current version
  end

  # Conditional GET
  def index
    @posts = Post.recent
    response.headers["Cache-Control"] = "public, max-age=300"
  end
end

Common Mistakes

1. Caching Too Broadly

Caching the entire page prevents showing dynamic content (user-specific data). Use fragment caching instead.

2. Not Expiring Caches

Cached data becomes stale. Set expires_in appropriately or use automatic expiry via touch: true.

3. Cache Key Collisions

Without proper cache keys, different content uses the same cache entry. Rails auto-generates keys from model name, id, and updated_at.

4. Not Using Redis in Production

File-based caching (default) is slow and doesn't scale across servers. Use Redis or Memcached.

5. Forgetting to Set Cache Headers

HTTP caching (ETags, Cache-Control) reduces server load from returning clients. Always configure.

Practice Questions

1. What is fragment caching?

Caches parts of a view template. <% cache post do %> caches the rendered HTML for a post record.

2. What is Russian doll caching?

Nested fragment caching. Post fragment contains child comment fragments. Invalidating the parent also invalidates children.

3. How do you manually cache data?

Use Rails.cache.fetch("key", expires_in: 1.hour) { expensive_query } for low-level caching.

4. What are ETags?

HTTP headers that uniquely identify a resource version. Clients send the ETag and get 304 Not Modified if unchanged.

5. Challenge: Implement Russian doll caching for a blog with posts and comments.

<% cache @post do %>
  <h1><%= @post.title %></h1>
  <%= render @post.comments %>  <!-- Each in _comment.html.erb with <% cache comment do %> -->
<% end %>

FAQ

How does Rails know when to expire a cache?

Model-based cache keys include updated_at. When updated_at changes (save, touch), the cache key changes and old entries are invalidated.

Should I use Redis or Memcached for caching?

Redis supports data persistence, pub/sub, and works as both cache and data store. Memcached is simpler and faster for pure caching.

What is the difference between page and action caching?

Page caching serves the entire page bypassing Rails entirely. Action caching runs before_action filters but caches the response.

How do I cache API responses?

Use HTTP caching (ETags, Cache-Control) and fragment caching for serialized JSON. Or use low-level caching with Redis.

How do I invalidate all caches when data changes?

Add touch: true to belongs_to associations. This updates the parent's updated_at, changing its cache key.

Mini Project: Blog Caching

Implement fragment caching with automatic expiry for a blog.

<% cache @post do %>
  <h1><%= @post.title %></h1>
  <p><%= @post.body %></p>
  <p>Updated: <%= @post.updated_at %></p>
<% end %>

What's Next

Ruby on Rails Security Ruby on Rails Docker

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro