Specifies the adapter Composite KV API.
Composite operations combine a read and a write on the same key in one
call and receive a function as an argument. This behaviour covers
get_and_update/6, update/7, fetch_or_store/6, and
get_or_store/6.
By default, Nebulex builds these operations on top of the
Nebulex.Adapter.KV primitives (fetch, put, and delete), and the
given function runs in the calling process, on the local node. This holds
even when the adapter performs the underlying read and write commands on
remote nodes.
Because the read and the write are separate commands, the default implementation is not atomic:
For
get_and_update/6andupdate/7, concurrent calls on the same key can overwrite each other's changes.For
fetch_or_store/6andget_or_store/6, concurrent cache misses on the same key can evaluate the function more than once, and the last write wins.
If atomicity is required and the adapter supports transactions, wrap the
call in Nebulex.Cache.transaction/2 locking the key with the :keys
option. The lock coordinates only writers that use transactions on the same
keys; plain writes are not affected.
This behaviour is required: Nebulex.Cache validates at compile time that
the adapter implements it, alongside Nebulex.Adapter and
Nebulex.Adapter.KV. Most adapters get it with
use Nebulex.Adapter.CompositeKV, which provides the default implementation
in this module. An adapter implements the callbacks itself to change where
the function runs (e.g., on the node owning the key) or to provide different
atomicity guarantees.
Default implementation
use Nebulex.Adapter.CompositeKV declares the behaviour and provides the
default implementation for every callback. The callbacks are overridable,
so an adapter can override only some of them:
defmodule MyAdapter do
@behaviour Nebulex.Adapter
@behaviour Nebulex.Adapter.KV
use Nebulex.Adapter.CompositeKV
# Override `get_and_update/6` and `update/7`; the other callbacks
# fall back to the default implementation.
@impl true
def get_and_update(adapter_meta, key, fun, ttl, keep_ttl?, opts) do
# Adapter-specific implementation ...
end
@impl true
def update(adapter_meta, key, initial, fun, ttl, keep_ttl?, opts) do
# Adapter-specific implementation ...
end
...
endTelemetry
Each composite operation is a cache command itself: a Telemetry span with
command: :get_and_update, command: :update, command: :fetch_or_store,
or command: :get_or_store is emitted. The default implementation runs the
primitive commands through Nebulex.Adapter.run_command/4, so each of them
emits its own command span as well, and the cache entry events and stats
built on those spans are still produced. The shared :telemetry,
:telemetry_event, and :telemetry_metadata options apply to the composite
command span and are forwarded to the primitive commands.
Overrides and cache entry events
The built-in cache entry (Nebulex.Event.CacheEntryEvent) and stats
handlers are driven by the primitive command events, not by the composite
one. If an override bypasses those commands, or runs them under another
cache's metadata, those handlers will not account for the operation on
the original cache. Preserve equivalent events for the affected cache
when implementing an override.
Summary
Types
Proxy type to the adapter meta
Callbacks
Fetches the value for key or, on a cache miss, evaluates fun and
stores its result.
Gets the value for key and updates it using the given function.
Gets the value for key or, on a cache miss, evaluates fun and stores
whatever it returns.
Updates the cached key with the given function.
Functions
Default implementation for fetch_or_store/6.
Default implementation for get_and_update/6.
Default implementation for get_or_store/6.
Default implementation for update/7.
Types
@type adapter_meta() :: Nebulex.Adapter.adapter_meta()
Proxy type to the adapter meta
Callbacks
@callback fetch_or_store( adapter_meta(), Nebulex.Cache.key(), Nebulex.Cache.fetch_or_store_fun(), Nebulex.Cache.ttl(), Nebulex.Cache.keep_ttl(), Nebulex.Cache.opts() ) :: Nebulex.Cache.ok_error_tuple(Nebulex.Cache.value())
Fetches the value for key or, on a cache miss, evaluates fun and
stores its result.
fun must return {:ok, value}, in which case value is stored under
key and returned, or {:error, reason}, in which case nothing is
stored and the error is returned.
The ttl and keep_ttl arguments apply to the write, as in
Nebulex.Adapter.KV.put/7.
Returns {:ok, value} if successful; {:error, reason} otherwise.
@callback get_and_update( adapter_meta(), Nebulex.Cache.key(), Nebulex.Cache.get_and_update_fun(), Nebulex.Cache.ttl(), Nebulex.Cache.keep_ttl(), Nebulex.Cache.opts() ) :: Nebulex.Cache.ok_error_tuple({Nebulex.Cache.value(), Nebulex.Cache.value()})
Gets the value for key and updates it using the given function.
fun is called with the current cached value under key (or nil if
key hasn't been cached) and must return a two-element tuple: the value to
return as the current value, which may differ from the cached one, and the
new value to store under key. fun may also return :pop to remove the
entry and return the current value.
The ttl and keep_ttl arguments apply to the write, as in
Nebulex.Adapter.KV.put/7.
Returns {:ok, {current_value, new_value}} if successful;
{:error, reason} otherwise.
@callback get_or_store( adapter_meta(), Nebulex.Cache.key(), Nebulex.Cache.get_or_store_fun(), Nebulex.Cache.ttl(), Nebulex.Cache.keep_ttl(), Nebulex.Cache.opts() ) :: Nebulex.Cache.ok_error_tuple(Nebulex.Cache.value())
Gets the value for key or, on a cache miss, evaluates fun and stores
whatever it returns.
The ttl and keep_ttl arguments apply to the write, as in
Nebulex.Adapter.KV.put/7.
Returns {:ok, value} if successful; {:error, reason} otherwise.
@callback update( adapter_meta(), Nebulex.Cache.key(), initial :: Nebulex.Cache.value(), Nebulex.Cache.update_fun(), Nebulex.Cache.ttl(), Nebulex.Cache.keep_ttl(), Nebulex.Cache.opts() ) :: Nebulex.Cache.ok_error_tuple(Nebulex.Cache.value())
Updates the cached key with the given function.
If key is present in the cache, fun is invoked with the current value
and its result is stored under key. If key is not present, initial
is stored under key and fun is not invoked.
The ttl and keep_ttl arguments apply to the write, as in
Nebulex.Adapter.KV.put/7.
Returns {:ok, value} with the stored value if successful;
{:error, reason} otherwise.
Functions
@spec fetch_or_store( adapter_meta(), Nebulex.Cache.key(), Nebulex.Cache.fetch_or_store_fun(), Nebulex.Cache.ttl(), Nebulex.Cache.keep_ttl(), Nebulex.Cache.opts() ) :: Nebulex.Cache.ok_error_tuple(Nebulex.Cache.value())
Default implementation for fetch_or_store/6.
@spec get_and_update( adapter_meta(), Nebulex.Cache.key(), Nebulex.Cache.get_and_update_fun(), Nebulex.Cache.ttl(), Nebulex.Cache.keep_ttl(), Nebulex.Cache.opts() ) :: Nebulex.Cache.ok_error_tuple({Nebulex.Cache.value(), Nebulex.Cache.value()})
Default implementation for get_and_update/6.
@spec get_or_store( adapter_meta(), Nebulex.Cache.key(), Nebulex.Cache.get_or_store_fun(), Nebulex.Cache.ttl(), Nebulex.Cache.keep_ttl(), Nebulex.Cache.opts() ) :: Nebulex.Cache.ok_error_tuple(Nebulex.Cache.value())
Default implementation for get_or_store/6.
@spec update( adapter_meta(), Nebulex.Cache.key(), Nebulex.Cache.value(), Nebulex.Cache.update_fun(), Nebulex.Cache.ttl(), Nebulex.Cache.keep_ttl(), Nebulex.Cache.opts() ) :: Nebulex.Cache.ok_error_tuple(Nebulex.Cache.value())
Default implementation for update/7.