Skip to content

Commit

Permalink
Update and neaten some warming functions
Browse files Browse the repository at this point in the history
  • Loading branch information
whitfin committed Mar 22, 2024
1 parent 5754f3e commit 63d1565
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 47 deletions.
2 changes: 1 addition & 1 deletion lib/cachex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1478,7 +1478,7 @@ defmodule Cachex do
if async do
send(pid, :cachex_warmer)
else
GenServer.call(pid, :blocking_cachex_warmer, :infinity)
GenServer.call(pid, :cachex_warmer, :infinity)
end
end
end
Expand Down
51 changes: 28 additions & 23 deletions lib/cachex/warmer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,18 @@ defmodule Cachex.Warmer do
#
# Initialization will trigger an initial cache warming, and store
# the provided state for later to provide during further warming.
def init({cache(name: name), warmer(state: state)}) do
{:ok, {name, state, nil}}
def init({cache(name: cache), warmer(state: state)}) do
{:ok, {cache, state, nil}}
end

def handle_call(:blocking_cachex_warmer, _from, process_state) do
{:reply, :ok, execute_warmer(process_state)}
end

def handle_info(:cachex_warmer, process_state) do
{:noreply, execute_warmer(process_state)}
@doc false
# Warms a number of keys in a cache.
#
# This is a blocking binding to `handle_info(:cache_warmer)`. See
# the documentation of that implementation for more information.
def handle_call(:cachex_warmer, _from, state) do
{:noreply, new_state} = handle_info(:cachex_warmer, state)
{:reply, :ok, new_state}
end

@doc false
Expand All @@ -92,28 +94,31 @@ defmodule Cachex.Warmer do
# cache via `Cachex.put_many/3` if returns in a Tuple tagged with the
# `:ok` atom. If `:ignore` is returned, nothing happens aside from
# scheduling the next execution of the warming to occur on interval.
defp execute_warmer({name, state, timer}) do
def handle_info(:cachex_warmer, {cache, state, timer}) do
# clean our any existing timers
if timer, do: Process.cancel_timer(timer)

# execute, passing state
Cachex.execute(name, fn cache ->
case execute(state) do
# no changes
:ignore ->
:ignore
case execute(state) do
# no changes
:ignore ->
:ignore

# set pairs without options
{:ok, pairs} ->
Cachex.put_many(cache, pairs)
# set pairs without options
{:ok, pairs} ->
Cachex.put_many(cache, pairs)

# set pairs with options
{:ok, pairs, options} ->
Cachex.put_many(cache, pairs, options)
end
end)
# set pairs with options
{:ok, pairs, options} ->
Cachex.put_many(cache, pairs, options)
end

# trigger the warming to happen again after the interval
{name, state, :erlang.send_after(interval(), self(), :cachex_warmer)}
new_timer = :erlang.send_after(interval(), self(), :cachex_warmer)
new_state = {cache, state, new_timer}

# pass the new state
{:noreply, new_state}
end
end
end
Expand Down
44 changes: 21 additions & 23 deletions test/cachex/warmer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -77,33 +77,31 @@ defmodule Cachex.WarmerTest do
assert Cachex.get!(cache, "state") == state
end

test "warmer triggers hook for sync and async warmers" do
expected_values = [{1, 1}]
expected_opts = [ttl: 1_000]

warmer_state = %{
expected_values: expected_values,
expected_opts: expected_opts
}

hook = ForwardHook.create()
test "triggering cache hooks from within warmers" do
# create a test warmer to pass to the cache
Helper.create_warmer(:hook_warmer_async, 15000, fn _ ->
{:ok, [{1, 1}]}
end)

# create a test warmer to pass to the cache
Helper.create_warmer(:test_warmer, 500, fn state ->
{:ok, state.expected_values, state.expected_opts}
Helper.create_warmer(:hook_warmer_sync, 15000, fn _ ->
{:ok, [{2, 2}]}
end)

for async <- [true, false] do
# create a cache instance with a warmer and hook
Helper.create_cache(
warmers: [
warmer(module: :test_warmer, state: warmer_state, async: async)
],
hooks: [hook]
)
# create a hook to forward messages
hook = ForwardHook.create()

assert_receive {{:put_many, [^expected_values, ^expected_opts]},
{:ok, true}}
end
# create a cache instance with a warmer and hook
Helper.create_cache(
hooks: [hook],
warmers: [
warmer(module: :hook_warmer_async, async: true),
warmer(module: :hook_warmer_sync, async: false)
]
)

# ensure that we receive the creation of both warmers
assert_receive({{:put_many, [[{1, 1}], []]}, {:ok, true}})
assert_receive({{:put_many, [[{2, 2}], []]}, {:ok, true}})
end
end

0 comments on commit 63d1565

Please sign in to comment.