102 lines
2.4 KiB
Elixir
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
|