Current section

Files

Jump to
cachex lib cachex actions take.ex
Raw

lib/cachex/actions/take.ex

defmodule Cachex.Actions.Take do
@moduledoc false
# Command module to allow taking of cache entries.
#
# The notion of taking a key is the act of retrieving a key and deleting it
# in a single atomic action. It's useful when used to guarantee that a given
# process retrieves the final value of an entry.
#
# Taking a key is clearly destructive, so it operates in a lock context.
alias Cachex.Services.Informant
alias Cachex.Services.Janitor
alias Cachex.Services.Locksmith
# we need our imports
import Cachex.Spec
##############
# Public API #
##############
@doc """
Takes an entry from a cache.
This will always remove the entry from the cache, ensuring that the entry no
longer exists immediately after this call finishes.
Expirations are lazily checked here, ensuring that even if a value is taken
from the cache it's only returned if it has not yet expired.
Taking a value happens in a lock aware context to ensure that the key isn't
being currently locked by another write sequence.
"""
def execute(cache(name: name) = cache, key, _options) do
Locksmith.write(cache, [ key ], fn ->
name
|> :ets.take(key)
|> handle_take(cache)
end)
end
###############
# Private API #
###############
# Handles the result of taking a key from the backing table.
#
# If an entry comes back from the call, we check for expiration before returning
# back to the caller. If the entry has expired, we broadcast the expiry (as the
# entry was already removed when we took if from the cache).
defp handle_take([ entry(value: value) = entry ], cache) do
case Janitor.expired?(cache, entry) do
false ->
{ :ok, value }
true ->
Informant.broadcast(
cache,
const(:purge_override_call),
const(:purge_override_result)
)
{ :ok, nil }
end
end
defp handle_take([], _cache),
do: { :ok, nil }
end