Caching Strategies in Ruby & Rails
Effective caching reduces load, keeps content snappy, and buys time during spikes. Choose the right cache, fingerprint keys carefully, and monitor hit ratios to avoid stale data surprises.
Cache selection matrix
| Layer | Use When | Invalidation strategy |
|---|---|---|
| Memoization | Per-request calculations or expensive presenter logic | Reset object state; memoized value dies with request |
| Fragment cache | Reusable HTML partials (navigation, dashboards) | Version keys with timestamps or cache digests |
| Low-level cache (Redis/Memcached) | API responses, aggregated statistics, rendered Markdown | Manual invalidation + TTLs + cache version constants |
| HTTP CDN (Fastly/CloudFront) | Static files, public JSON, screenshots | Purge on deploy / use surrogate keys |
Invalidating caches safely
- Embed a version identifier in keys (`lesson_feed:v#{Lesson.maximum(:updated_at).to_i}`).
- Expire caches from callbacks or background jobs when underlying records change.
- For CDN caches, send
Fastly-Soft-Purgeheader so stale assets revalidate quickly. - Use cache tags (if supported) to invalidate related keys in bulk (e.g., all content for a specific course).
Example: warm lesson recommendations
class LessonRecommendations
CACHE_NAMESPACE = "lesson-recos".freeze
def self.prewarm!
User.active.find_each(batch_size: 200) do |user|
new(user).call
end
end
def initialize(user)
@user = user
end
def call
Rails.cache.fetch(cache_key, expires_in: 6.hours, race_condition_ttl: 10.seconds) do
Recommendations::NextLessons.new(@user).call
end
end
private
def cache_key
[CACHE_NAMESPACE, @user.id, @user.lessons.maximum(:updated_at)&.to_i].compact.join(":")
end
end
Use race_condition_ttl to prevent thundering herds, and schedule prewarm! during low-traffic windows.
Monitoring & hygiene
- Record cache hit/miss metrics (`ActiveSupport::Notifications.subscribe("cache_read.active_support")`).
- Alert when hit rate drops or Redis memory approaches configured maxmemory.
- Log cache invalidations during deploys to correlate with downstream metrics.
- Periodically audit large keys (`redis-cli --bigkeys`) to catch unbounded caches.