Packages

A distributed cache library for Elixir with pluggable backends, topologies, and near-cache support.

Current section

Files

Jump to
vela_cache lib vela ttl manager.ex
Raw

lib/vela/ttl/manager.ex

defmodule Vela.TTL.Manager do
@moduledoc """
Periodically sweeps the cache and removes expired entries.
Uses the TTL index table (an ordered_set keyed by {expires_at, key})
to find expired entries in O(expired) time instead of scanning everything.
"""
use GenServer
# ---- Public API ----
def start_link(opts) do
name = Keyword.fetch!(opts, :name)
GenServer.start_link(__MODULE__, opts, name: manager_name(name))
end
def manager_name(cache_name), do: :"vela_ttl_manager_#{cache_name}"
# ---- GenServer Callbacks ----
@impl true
def init(opts) do
config = Keyword.fetch!(opts, :config)
# Schedule the first sweep
ref = schedule_sweep(config.sweep_interval)
state = %{
config: config,
sweep_ref: ref
}
{:ok, state}
end
@impl true
def handle_info(:sweep, state) do
now = System.monotonic_time(:millisecond)
backend = state.config.backend
backend_state = get_backend_state(state.config.name)
count =
case backend.flush_expired(backend_state, now) do
{:ok, count, _new_bs} -> count
_ -> 0
end
:telemetry.execute(
state.config.telemetry_prefix ++ [:ttl_sweep, :stop],
%{evicted: count},
%{cache: state.config.name}
)
# Schedule the next sweep
ref = schedule_sweep(state.config.sweep_interval)
{:noreply, %{state | sweep_ref: ref}}
end
defp schedule_sweep(interval) do
Process.send_after(self(), :sweep, interval)
end
defp get_backend_state(cache_name) do
ts = :persistent_term.get({:vela_topology_state, cache_name})
ts.backend_state
end
end