this repo has no description

Implement cache refresher

It will periodiically refresh cache with new top stories and prune the
old stories.

hauleth.dev c93daeea e1536ff7

verified
Changed files
+57
lib
+3
lib/esl_hn/application.ex
···
@impl true
def start(_type, _args) do
+
refresh = Application.fetch_env!(:esl_hn, :refresh)
+
children = [
EslHnWeb.Telemetry,
{Phoenix.PubSub, name: EslHn.PubSub},
{EslHn.Cache, name: EslHn},
+
{EslHn.Refresher, refresh: refresh, cache: {EslHn.Cache, EslHn}},
EslHnWeb.Endpoint
]
+54
lib/esl_hn/refresher.ex
···
+
defmodule EslHn.Refresher do
+
use GenServer
+
+
alias EslHn.Hn
+
+
def start_link(opts),
+
do: GenServer.start_link(__MODULE__, opts)
+
+
@impl true
+
def init(opts) do
+
refresh = Access.fetch!(opts, :refresh)
+
{mod, tid} = Access.fetch!(opts, :cache)
+
+
{:ok,
+
%{
+
refresh: refresh,
+
ids: MapSet.new(),
+
cache: {mod, tid}
+
}, {:continue, :refresh}}
+
end
+
+
@impl true
+
def handle_info(:refresh, state) do
+
{:noreply, state, {:continue, :refresh}}
+
end
+
+
@impl true
+
def handle_continue(:refresh, state) do
+
{mod, tid} = state.cache
+
+
with {:ok, ids} <- Hn.top_stories_ids(),
+
new_set = MapSet.new(ids),
+
new = MapSet.difference(new_set, state.ids),
+
old = MapSet.difference(state.ids, new_set),
+
{:ok, stories} <- Hn.stories(new) do
+
rows =
+
for story <- stories, do: {story.id, story}
+
+
mod.write(tid, :index, ids)
+
+
:ok =
+
mod.write_all(tid, rows)
+
+
:ok =
+
mod.prune_all(tid, old)
+
+
EslHn.broadcast_new(for story <- stories, story.id in new, do: story)
+
end
+
+
Process.send_after(self(), :refresh, state.refresh)
+
+
{:noreply, state}
+
end
+
end