Rails caching is a great tool for scaling websites. We can use different cache stores (Redis and Memcached being common choices).

Using key based cache frees us from writing observers to manually purge the cache. When record is updated it’s cache_key changes, new content is cached and old one is eventually purged using TTL. Here is my previous post about various uses of caching.

To enable it we modify production.rb.

config.cache_store = :readthis_store,
{ expires_in: 1.hour,
namespace: app_cache,
redis: { host: 'host_name', port: 6379, db: 0 },
driver: :hiredis }

Here is a basic CMS with Articles and Comments.

# app/models/article.rb
class Article
  field :body
  has_many :comments
  def comments_count
    Rails.cache.fetch([cache_key, __method__]) do
      comments.count
    end
  end
  def another_method
    Rails.cache.fetch([cache_key, __method__]) do
      ...
    end
  end
end
# app/models/comment.rb
class Comment
  field :body
  belongs_to :article, touch: true
end

We cache comments_count and use touch: true to update Article timestamp when new Comment is created/updated/deleted. The problem is it busts cached data for ALL Article methods and view cache as well. We might not want that.

In such cases instead of touch: true we can implement callbacks on the child record to delete specific cached data for the parent record.

# app/models/comment.rb
class Comment
  field :body
  belongs_to :article
  after_create  :article_comments_count
  after_destroy :article_comments_count
private
  def article_comments_count
    cache_key = [article.cache_key, 'comments_count']
    Rails.cache.delete(cache_key)
  end
end

This will not impact Article timestamp and leave the other cached data in place. We do need to be more careful with this approach as it could lead to situations where only some cached data is deleted but some remains stale until default application TTL removes it. But this can be a useful solution where there is unnecessary cache purging and recreation.