Cache SWR
Server-side stale-while-revalidate caching for Rails.
About
Cache SWR stores a value plus two time windows: a fresh window and a stale window. Within the fresh window, values are returned immediately. Within the stale window, stale values are returned while a refresh runs in the background (or inline if you prefer).
This pattern reduces tail latency and keeps caches warm without blocking callers.
Use Cases
- Reduce p99 latency for expensive reads with background refreshes
- Serve dashboards and reports without blocking on cache refresh
- Keep hot data warm while avoiding request pileups
- Provide smoother cache behavior under load
Compatibility
- Ruby 3.0+
- ActiveSupport 6.1+
- Works with ActiveSupport cache stores
- Redis-backed stores are recommended when locking is enabled
Elysium Arc Reliability Toolkit
Also check out these related gems:
- Cache Coalescer: https://github.com/Elysium-Arc/cache-coalescer
- Faraday Hedge: https://github.com/Elysium-Arc/faraday-hedge
- Rack Idempotency Kit: https://github.com/Elysium-Arc/rack-idempotency-kit
- Env Contract: https://github.com/Elysium-Arc/env-contract
Installation
# Gemfile
gem "cache-swr"Usage
value = Cache::SWR.fetch("expensive-key", ttl: 60, swr: 300, store: Rails.cache) do
ExpensiveQuery.call
endRails integration adds Rails.cache.fetch_swr:
Rails.cache.fetch_swr("expensive-key", ttl: 60, swr: 300) { ExpensiveQuery.call }If you are using an in-memory store, disable locking:
Cache::SWR.fetch("key", ttl: 30, swr: 120, store: ActiveSupport::Cache::MemoryStore.new, lock: false) do
compute
endOptions
-
ttl(Integer) fresh window in seconds -
swr(Integer) stale window in seconds -
refresh(:async,:sync, ornil) refresh strategy -
lock(Boolean) enable or disable locking -
lock_ttl(Integer) lock expiry in seconds -
lock_clientRedis client for custom locking -
storeActiveSupport cache store (defaults toRails.cachewhen available)
Notes
- During the SWR window, stale values are served while a refresh runs.
-
refresh: :asyncuses a background thread; choose:syncfor deterministic refresh. - When
lockis enabled, the store must exposeredisor you must providelock_client.
Release
bundle exec rake release