84 lines
2.3 KiB
Elixir
84 lines
2.3 KiB
Elixir
defmodule Plausible.Cache.Warmer do
|
|
@moduledoc """
|
|
A periodic cache warmer.
|
|
|
|
Child specification options available:
|
|
|
|
* `cache_impl` - module expected to implement `Plausible.Cache` behaviour
|
|
* `interval` - the number of milliseconds for each warm-up cycle
|
|
* `cache_name` - defaults to cache_impl.name() but can be overridden for testing
|
|
* `force_start?` - enforcess process startup for testing, even if it's barred
|
|
by `Plausible.Cache.enabled?`. This is useful for avoiding issues with DB ownership
|
|
and async tests.
|
|
* `warmer_fn` - by convention, either `:refresh_all` or `:refresh_updated_recently`,
|
|
both are automatically provided by `cache_impl` module. Technically any exported
|
|
or captured function will work, if need be.
|
|
|
|
See tests for more comprehensive examples.
|
|
"""
|
|
|
|
@behaviour :gen_cycle
|
|
|
|
require Logger
|
|
|
|
@spec child_spec(Keyword.t()) :: Supervisor.child_spec() | :ignore
|
|
def child_spec(opts) do
|
|
child_name = Keyword.get(opts, :child_name, __MODULE__)
|
|
|
|
%{
|
|
id: child_name,
|
|
start: {:gen_cycle, :start_link, [{:local, child_name}, __MODULE__, opts]}
|
|
}
|
|
end
|
|
|
|
@impl true
|
|
def init_cycle(opts) do
|
|
cache_impl = Keyword.fetch!(opts, :cache_impl)
|
|
cache_name = Keyword.get(opts, :cache_name, cache_impl.name())
|
|
interval = Keyword.fetch!(opts, :interval)
|
|
|
|
warmer_fn =
|
|
case Keyword.fetch!(opts, :warmer_fn) do
|
|
f when is_function(f, 1) ->
|
|
f
|
|
|
|
f when is_atom(f) ->
|
|
true = function_exported?(cache_impl, f, 1)
|
|
Function.capture(cache_impl, f, 1)
|
|
end
|
|
|
|
force_start? = Keyword.get(opts, :force_start?, false)
|
|
|
|
if Plausible.Cache.enabled?() or force_start? do
|
|
Logger.notice(
|
|
"#{__MODULE__} initializing #{inspect(warmer_fn)} #{cache_name} with interval #{interval}..."
|
|
)
|
|
|
|
{:ok,
|
|
{interval,
|
|
opts
|
|
|> Keyword.put(:cache_name, cache_name)
|
|
|> Keyword.put(:warmer_fn, warmer_fn)}}
|
|
else
|
|
:ignore
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def handle_cycle(opts) do
|
|
cache_name = Keyword.fetch!(opts, :cache_name)
|
|
warmer_fn = Keyword.fetch!(opts, :warmer_fn)
|
|
|
|
Logger.notice("#{__MODULE__} running #{inspect(warmer_fn)} on #{cache_name}...")
|
|
|
|
warmer_fn.(opts)
|
|
|
|
{:continue_hibernated, opts}
|
|
end
|
|
|
|
@impl true
|
|
def handle_info(_msg, state) do
|
|
{:continue, state}
|
|
end
|
|
end
|