ActiveSupport::Cache
GitHub Overview
Topics
Star History
Library
ActiveSupport::Cache
Overview
ActiveSupport::Cache is a comprehensive caching framework built into Ruby on Rails.
Details
ActiveSupport::Cache is a sophisticated caching system provided as part of the Ruby on Rails framework. It supports multiple cache stores (memory, file, Redis, database, etc.) and offers a unified API for cache operations. The framework provides essential methods like read, write, delete, exist?, and fetch, along with advanced features such as automatic cache key generation, expiration management, and hierarchical cache invalidation. Starting with Rails 7, database-backed Solid Cache is available by default, enabling stable caching in distributed environments. The system comprehensively provides functionality necessary for web application performance improvement, including fragment caching, SQL query caching, and page caching.
Pros and Cons
Pros
- Unified API: Operate multiple cache stores through the same interface
- Rich Store Options: Diverse choices including memory, file, Redis, and database
- Rails Integration: Seamless usage through deep framework integration
- Automatic Key Management: Automatic cache key generation and collision avoidance
- Hierarchical Invalidation: Cache clearing functionality based on dependencies
- Encryption Support: Secure data caching capabilities
- Distributed Support: Horizontal scaling through sharding functionality
Cons
- Rails Dependency: Limited usage outside Rails projects
- Learning Curve: Need to master configuration options and optimization techniques
- Memory Usage: Important memory management when caching large amounts of data
- Debug Complexity: Identifying cache-related issues can be challenging
- Performance Dependency: Improper usage may actually degrade performance
Key Links
- Rails Caching Guide
- ActiveSupport::Cache API
- Solid Cache
- Rails GitHub Repository
- Complete Rails Caching Guide
Code Examples
Basic Cache Operations
# Writing to cache
Rails.cache.write("user:123", user_data, expires_in: 1.hour)
# Reading from cache
cached_user = Rails.cache.read("user:123")
# Existence check
if Rails.cache.exist?("user:123")
puts "Cache exists"
end
# Deleting cache
Rails.cache.delete("user:123")
Utilizing the fetch Method
# Retrieve from cache if exists, otherwise execute block and save to cache
user = Rails.cache.fetch("user:#{id}", expires_in: 12.hours) do
User.find(id)
end
# Conditional caching
expensive_data = Rails.cache.fetch("calculation:#{params}", expires_in: 1.day) do
# Heavy computation process
perform_expensive_calculation(params)
end
# Nested cache keys
Rails.cache.fetch(["user", user.id, "profile", user.updated_at]) do
user.profile_data
end
Bulk Operations with Multiple Keys
# Read multiple caches at once
user_ids = [1, 2, 3, 4, 5]
cache_keys = user_ids.map { |id| "user:#{id}" }
cached_users = Rails.cache.read_multi(*cache_keys)
# Write multiple caches at once
users_data = {
"user:1" => user1_data,
"user:2" => user2_data,
"user:3" => user3_data
}
Rails.cache.write_multi(users_data, expires_in: 2.hours)
# Delete multiple caches at once
Rails.cache.delete_multi("user:1", "user:2", "user:3")
Fragment Caching
<!-- Fragment caching in ERB templates -->
<% cache ["user_profile", @user, @user.updated_at] do %>
<div class="user-profile">
<h2><%= @user.name %></h2>
<p><%= @user.bio %></p>
<!-- Heavy rendering process -->
</div>
<% end %>
<!-- Conditional fragment caching -->
<% cache_if @user.cache_enabled?, ["user_data", @user] do %>
<div class="user-data">
<%= render 'expensive_partial' %>
</div>
<% end %>
Custom Cache Store Configuration
# config/environments/production.rb
# Redis cache store configuration
config.cache_store = :redis_cache_store, {
url: ENV['REDIS_URL'],
connect_timeout: 30,
read_timeout: 0.2,
write_timeout: 0.2,
reconnect_attempts: 1,
namespace: 'cache'
}
# File cache store configuration
config.cache_store = :file_store, '/var/cache/rails', {
namespace: 'app_cache',
expires_in: 1.hour
}
# Memory cache store configuration (development environment)
config.cache_store = :memory_store, {
size: 64.megabytes,
namespace: 'development_cache'
}
Solid Cache Configuration and Usage
# config/cache.yml
production:
database: cache_production
store_options:
max_age: <%= 2.weeks.to_i %>
max_entries: 1_000_000
namespace: <%= Rails.env %>
encrypt: true
development:
database: cache_development
store_options:
max_size: <%= 256.megabytes %>
namespace: development
# config/environments/production.rb
config.cache_store = :solid_cache_store
# Solid Cache specific configuration
config.solid_cache.size_estimate_samples = 1000
config.solid_cache.encrypt = true
config.solid_cache.key_hash_stage = :indexed
Advanced Caching Patterns
class UserService
def self.fetch_user_with_associations(user_id)
Rails.cache.fetch("user_full:#{user_id}", expires_in: 1.hour) do
User.includes(:profile, :posts, :followers)
.find(user_id)
.as_json(include: [:profile, :posts, :followers])
end
end
def self.invalidate_user_cache(user_id)
# Bulk delete related caches
Rails.cache.delete_matched("user*:#{user_id}*")
Rails.cache.delete("user_count")
Rails.cache.delete("recent_users")
end
def self.warm_cache(user_id)
# Cache preloading
fetch_user_with_associations(user_id)
Rails.cache.fetch("user_stats:#{user_id}", expires_in: 6.hours) do
calculate_user_statistics(user_id)
end
end
end
# Cache integration in models
class User < ApplicationRecord
after_update :expire_cache
after_destroy :expire_cache
def cached_profile
Rails.cache.fetch(cache_key_with_version) do
profile.as_json
end
end
private
def expire_cache
Rails.cache.delete(cache_key_with_version)
Rails.cache.delete_matched("user*:#{id}*")
end
end