Current section

Files

Jump to
islands_board_cache lib islands board cache server.ex
Raw

lib/islands/board/cache/server.ex

defmodule Islands.Board.Cache.Server do
@moduledoc """
A process that loads a list of boards from an external source
and caches it for expedient access. The cache is automatically
refreshed every 10 minutes.
"""
use GenServer
use PersistConfig
alias __MODULE__
alias Islands.Board.Cache.{Loader, Log}
alias Islands.Board
@type from :: GenServer.from()
@type state :: {list, reference}
@path Application.get_env(@app, :board_set_path)
@refresh_interval :timer.minutes(10)
@spec start_link(term) :: GenServer.on_start()
def start_link(:ok), do: GenServer.start_link(Server, :ok, name: Server)
## Private functions
@spec schedule_refresh :: reference
defp schedule_refresh,
do: self() |> Process.send_after(:refresh, @refresh_interval)
## Callbacks
@spec init(term) :: {:ok, state}
def init(:ok), do: {:ok, {Loader.read_boards(), schedule_refresh()}}
@spec handle_call(term, from, term) :: {:reply, Board.t(), term}
def handle_call(:get_board, _from, {boards, _timer_ref} = state) do
{:reply, Enum.random(boards), state}
end
def handle_call(:board_count, _from, {boards, _timer_ref} = state) do
{:reply, length(boards), state}
end
@spec handle_cast(term, term) :: {:noreply, term}
def handle_cast({:persist_board, board}, state) do
File.write(
@path,
case File.read(@path) do
{:ok, binary} -> :erlang.binary_to_term(binary)
{:error, _reason} -> MapSet.new()
end
|> MapSet.put(board)
|> :erlang.term_to_binary()
)
:ok = Log.info(:board_persisted, {@path, board})
{:noreply, state}
end
@spec handle_info(term, state) :: {:noreply, state}
def handle_info(:refresh, {_boards, timer_ref} = _state) do
Process.cancel_timer(timer_ref, info: false)
{:noreply, {Loader.read_boards(), schedule_refresh()}}
end
end