Types of Caching in Rails

Discover various types of caching for performance enhancement in Rails

Tushar Adhao
4 min readAug 24, 2022

As per the application requirement, the caching types could be divided into various types.

  • Page and Action caching
  • Fragment Caching (with Russian Doll caching)
  • Low level caching

There could be further different subsections to the caching types but these are the main ones to understand which are described as follows.

Page and Action caching

Page caching is the type of caching in which the HTML page gets cached so that the web server (ex. Apache, NGINX) serves the request (i.e. HTML page gets cached) without hitting the backend server/action. The con to it is any filter (like authentication filter, etc.) on the action will not work properly (since no backend hit).

The Action caching works similar to page caching except that the request hits the backend server to perform the filter attached to the action (ex. for the authentication, etc.) before the cache is served.

Note: After the release from Rails version 4, the page and action caching has been removed from core Rails and now we can use it with external gems as actionpack-page-caching and actionpack-action-caching

Fragment Caching (with Russian Doll caching)

This is one of the popular types of caching in which some particular sections from the page get cached. The example could be given as follows,

# 1. Instance being cached
<% @products.each do |product| %>
<% cache product do %>
<%= render product %>
<% end %>
<% end %>
# 2. or for partial caching
<%= render 'products/product', collection: product, cached: true %>

In the first example, the instances for the product get cached so that even with the page reloads, the cached value could get served. The cached data gets saved in the path containing hash digest format, ex. views/products/index:bem1237108094918eeba423456e786901/products/1so any change in the HTML page, in the sense that any change(specifically newly created or destroyed entries) in the Product would change the hash digest (as it gets calculated by the template tree digest), resulting in the new cached data being served and the previous caches will get deleted automatically.

Russian Doll

Russian Doll caching can be explained as a fragment inside another fragment caching. The example could be given as follows,

<% @products.each do |product| %>
<% cache product do %>
...
<% product.games.each do |game| %>
<% cache game do %>
<%= render game %>
<% end %>
<% end %>
<% end %>
<% end %>

The problem here is if the attribute for the game changes (respectively with its updated_at attribute), then the cache should expire to display new data, but since the parent product will have no change in its updated_at attribute, so its cache will not expire and the page still shows the old state. To remove this behavior we can resolve it by adding touch: true in the Game model below,

class Product < ApplicationRecord
has_many :games
end
class Game < ApplicationRecord
belongs_to :product, touch: true
end

Now any change in the game attribute will trigger the parent product attribute to get a change in its updated_at attribute and that’s how the issue gets resolved.

Low level caching

Low-level caching is basically dealing with key-value-based caching structures. This is a very commonly used caching type in which the key can be read or written as below,

Rails.cache.write('cache-key', 123)
Rails.cache.read('cache-key') # => 123
Rails.cache.read('unknown-cache-key') # => nil

The most common use case is using it with the fetch attribute which provides a wrapper around the block to be read or written as below,

Rails.cache.fetch('cached-key', expires_in: 12.hours) do
1234
end

Here expires_in is an optional parameter to set the expiry time for the cache block for the key. Once expired, the block will get re-executed and cache data will get generated again with the new expiration time.

Note: For setting unique key for the cache value, cache_key_with_version or cache_key methods are used. cache_key returns model_name and id (ex. random_model/7) where as cache_key_with_version returns additionally updated_at timestamp with model_name and id (ex. random_model/7–1661247982)

The next important section to understand is how to manage the cache (like how to store cache and cache storage types) to effectively avail the advantages of caching to improve the performance of the application. The next blog focuses on the same which will be an exploration of various Types of Cache-Store. So see you in the next blog!

--

--

Tushar Adhao

Software artist spreading nuggets of coding gold and sometimes philosophy too.