diff options
Diffstat (limited to 'lib/plugins/link/store.ex')
-rw-r--r-- | lib/plugins/link/store.ex | 84 |
1 files changed, 75 insertions, 9 deletions
diff --git a/lib/plugins/link/store.ex b/lib/plugins/link/store.ex index ea43070..4e2aa58 100644 --- a/lib/plugins/link/store.ex +++ b/lib/plugins/link/store.ex @@ -1,29 +1,95 @@ defmodule Nola.Plugins.Link.Store do + alias DialyxirVendored.Warnings.Apply + use GenServer + require Logger require Record import Ex2ms @type url() :: String.t() - Record.defrecord(:link, link: nil, at: nil) - @type link :: record(:link, link: String.t(), at: nil) + Record.defrecord(:link, link: nil, result: nil, at: nil) + @type link :: record(:link, link: url(), result: any(), at: nil) - Record.defrecord(:link_entry, key: nil, at: nil) - @type link_entry :: record(:link_entry, key: {url(), String.t()}, at: nil) + Record.defrecord(:link_seen, key: nil, at: nil) + + @doc "A `link_seen` record represents a link that has been seen at a specific time in a given context." + @type link_seen :: record(:link_seen, key: {url(), String.t()}, at: nil) def setup do :ets.new(:links, [:set, :public, :named_table, keypos: 2]) + :ets.new(:links_witness, [:set, :public, :named_table, keypos: 2]) end - @spec insert_link(url()) :: true - def insert_link(url) do - :ets.insert(:links, link(link: url, at: NaiveDateTime.utc_now() |> NaiveDateTime.to_unix())) + @spec insert_link(url(), any()) :: true + def insert_link(url, result) do + :ets.insert( + :links, + link(link: url, result: result, at: DateTime.utc_now() |> DateTime.to_unix()) + ) end - @spec get_link(url()) :: String.t() | nil + @spec get_link(url()) :: any() | nil def get_link(url) do case :ets.lookup(:links, url) do - [link] -> link + [link(result: result)] -> result [] -> nil end end + + @spec witness_link(url(), String.t()) :: boolean() + def inhibit_link?(url, key) do + case :ets.lookup(:links_witness, {url, key}) do + [_] -> true + [] -> false + end + end + + @spec witness_link(url(), String.t()) :: :ok | :inhibit + def witness_link(url, key) do + if inhibit_link?(url, key) do + :inhibit + else + :ets.insert( + :links_witness, + link_seen(key: {url, key}, at: DateTime.utc_now() |> DateTime.to_unix()) + ) + + :ok + end + end + + def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__) + + @doc false + @impl true + def init(_) do + setup() + env = Keyword.fetch!(Application.get_env(:nola, Nola.Plugins.Link, []), :store) + :erlang.send_after(env[:interval], self(), :expire) + {:ok, nil} + end + + @doc false + @impl true + def handle_info(:expire, state) do + env = Keyword.fetch!(Application.get_env(:nola, Nola.Plugins.Link, []), :store) + :erlang.send_after(env[:interval], self(), :expire) + ttl = env[:ttl] / 1000 + inhibit = env[:inhibit] / 1000 + now = DateTime.utc_now() |> DateTime.to_unix() + + links_evicted = + :ets.select_delete(:links, [ + {{:_, :_, :_, :"$1"}, [{:<, :"$1", now - ttl}], [true]} + ]) + + witness_evicted = + :ets.select_delete(:links_witness, [ + {{:_, :_, :"$1"}, [{:<, :"$1", now - inhibit}], [true]} + ]) + + Logger.debug("evicted #{links_evicted} links and #{witness_evicted} witnesses") + + {:noreply, state} + end end |