summaryrefslogtreecommitdiff
path: root/lib/plugins/link/store.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/plugins/link/store.ex')
-rw-r--r--lib/plugins/link/store.ex84
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