| 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 |
|
|