Files
chess-trainer/lib/chesstrainer/cache.ex
T

102 lines
2.4 KiB
Elixir

defmodule Chesstrainer.Cache do
@moduledoc """
Generic LRU cache.
## Examples
iex> start_link(:tablebase)
"""
use GenServer
@max_size 1000
@backup_interval_minutes 60
def start_link(table), do: GenServer.start_link(__MODULE__, table, name: via_tuple(table))
def put(table, key, value), do: GenServer.call(via_tuple(table), {:put, key, value})
def get(table, key), do: GenServer.call(via_tuple(table), {:get, key})
def delete(table, key), do: GenServer.call(via_tuple(table), {:delete, key})
def backup(table), do: GenServer.cast(via_tuple(table), :backup)
def show(table) do
ensure_table(table)
:ets.tab2list(table)
end
def size(table), do: :ets.info(table, :size)
@impl true
def init(table) do
{:ok, table, {:continue, :restore_and_backup}}
end
@impl true
def handle_continue(:restore_and_backup, table) do
create_table(table)
__MODULE__.Persist.restore(table)
:timer.send_interval(:timer.minutes(@backup_interval_minutes), :backup)
{:noreply, table}
end
@impl true
def handle_call({:put, key, value}, _from, table) do
ensure_table(table)
timestamp = System.system_time(:millisecond)
:ets.insert(table, {key, value, timestamp})
GenServer.cast(self(), :maybe_evict)
{:reply, :ok, table}
end
@impl true
def handle_call({:get, key}, _from, table) do
ensure_table(table)
case :ets.lookup(table, key) do
[{^key, value, _old_timestamp}] ->
timestamp = System.system_time(:millisecond)
:ets.insert(table, {key, value, timestamp})
{:reply, {:ok, value}, table}
[] ->
{:reply, :error, table}
end
end
@impl true
def handle_call({:delete, key}, _from, table) do
:ets.delete(table, key)
{:reply, :ok, table}
end
@impl true
def handle_cast(:maybe_evict, table) do
__MODULE__.Evict.maybe_evict_oldest(table, @max_size)
{:noreply, table}
end
@impl true
def handle_cast(:backup, table) do
__MODULE__.Persist.backup(table)
{:noreply, table}
end
@impl true
def handle_info(:backup, table) do
GenServer.cast(self(), :backup)
{:noreply, table}
end
defp via_tuple(table), do: {:via, Registry, {Chesstrainer.CacheRegistry, table}}
defp ensure_table(table) do
case :ets.whereis(table) do
:undefined -> create_table(table)
_ -> :ok
end
end
defp create_table(table), do: :ets.new(table, [:named_table, :public, :set])
end