Cache.RefreshAhead (elixir_cache v0.4.6)
View SourceRefresh-ahead caching strategy that proactively refreshes values before they expire.
Values are stored with a timestamp. On get, if the value is within the
refresh window (i.e. now - inserted_at >= ttl - refresh_before), the current
value is returned immediately and an async Task is spawned to refresh it.
Only keys that are actively read get refreshed — unread keys naturally expire from the underlying adapter.
Usage
defmodule MyApp.Cache do
use Cache,
adapter: {Cache.RefreshAhead, Cache.Redis},
name: :my_cache,
opts: [
uri: "redis://localhost:6379",
refresh_before: :timer.seconds(30)
]
def refresh(key) do
{:ok, fetch_fresh_value(key)}
end
endOptions
:refresh_before(pos_integer/0) - Required. Milliseconds before TTL expiry at which to trigger a background refresh.:on_refresh- Optional refresh callback. If not provided, the cache module must definerefresh/1.:lock_node_whitelist- Optional node whitelist for distributed refresh locks. Defaults to all connected nodes.
How It Works
put/5wraps the value as{value, inserted_at_ms, ttl_ms}before delegating.get/4unwraps the tuple. Ifnow - inserted_at >= ttl - refresh_before, a backgroundTaskis spawned to call the refresh callback with the key.- A per-cache ETS deduplication table (
:<name>_refresh_tracker) prevents multiple concurrent refresh tasks for the same key. - On successful refresh,
put/5is called with the new value and the same TTL, resetting the inserted_at timestamp.
Note: When
sandbox?: true, values are stored unwrapped. Refresh logic is bypassed entirely.