Mastering Multi‑Layer Caching in Rails with Nginx, Redis, and MySQL

This article explains six practical caching strategies—from client‑side HTTP 304 handling to Nginx static caching, full‑page, fragment, query, and database caches—using Ruby on Rails, Nginx, MySQL, and Redis to dramatically improve web application performance.

21CTO
21CTO
21CTO
Mastering Multi‑Layer Caching in Rails with Nginx, Redis, and MySQL

Using cache is a crucial step to improve application performance. This article summarizes various caching techniques for dynamic content using Nginx, Ruby on Rails, MySQL and Redis.

1. Client‑side cache – leverage HTTP 304 Not Modified and Rails' fresh_when method. Example:

class ArticlesController
  def show
    @article = Article.find(params[:id])
    fresh_when last_modified: @article.updated_at.utc, etag: @article
  end
end

Include user‑specific data in the ETag to avoid stale personalized pages.

2. Nginx cache – cache static‑like resources. Two approaches:

A. Turn dynamic responses into static files via an after_filter.

class CategoriesController < ActionController::Base
  after_filter :generate_static_file, only: [:index]

  def index
    @categories = Category.all
  end

  def generate_static_file
    File.open(Rails.root.join('public', 'categories'), 'w') { |f| f.write response.body }
  end
end

Delete the file on updates.

class Category < ActiveRecord::Base
  after_save :delete_static_file
  after_destroy :delete_static_file

  def delete_static_file
    File.delete Rails.root.join('public', 'categories')
  end
end

B. Store rendered pages in Redis and let Nginx fetch them.

upstream redis {
  server redis_server_ip:6379;
}
location /categories {
  set $redis_key $uri;
  default_type text/html;
  redis_pass redis;
  error_page 404 = @httpapp;
}
location @httpapp {
  proxy_pass http://ruby_backend;
}

Use redis_cache.set and redis_cache.setex to manage cache entries.

3. Full‑page cache – cache entire responses with varying parameters, e.g., pagination.

class CategoriesController
  caches_action :index, expires_in: 1.day, cache_path: proc { "categories/index/#{params[:page].to_i}" }
end

4. Fragment cache – the most versatile. Examples for ads, article bodies, complex pages, conditional caching, and touch‑based invalidation.

- cache "adverts/#{request.controller_name}/#{request.action_name}", expires_in: 1.day do
  - if advert = Advert.where(name: request.controller_name + request.action_name, enable: true).first
    div.ad = advert.content
end
- cache "articles/#{@article.id}/#{@article.updated_at.to_i}" do
  div.article = @article.content.markdown2html
end
def cache_if(condition, name = {}, cache_options = {}, &block)
  if condition
    cache(name, cache_options, &block)
  else
    yield
  end
end

5. Query cache – Rails query cache for a single request and gems like identity_cache or second_level_cache for cross‑request caching.

class User < ActiveRecord::Base
  include IdentityCache
end
class User < ActiveRecord::Base
  acts_as_cached version: 1, expires_in: 1.week
end

6. Database cache – summarises the six layers of caching from client to server.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performanceRediscachingWeb DevelopmentRails
21CTO
Written by

21CTO

21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.