Nebulex.Adapter.CompositeKV behaviour (Nebulex v3.1.0)

Copy Markdown View Source

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:

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

  ...
end

Telemetry

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.

Types

adapter_meta()

@type adapter_meta() :: Nebulex.Adapter.adapter_meta()

Proxy type to the adapter meta

Callbacks

fetch_or_store(adapter_meta, key, fetch_or_store_fun, ttl, keep_ttl, opts)

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.

See Nebulex.Cache.fetch_or_store/3.

get_and_update(adapter_meta, key, get_and_update_fun, ttl, keep_ttl, opts)

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.

See Nebulex.Cache.get_and_update/3.

get_or_store(adapter_meta, key, get_or_store_fun, ttl, keep_ttl, opts)

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.

See Nebulex.Cache.get_or_store/3.

update(adapter_meta, key, initial, update_fun, ttl, keep_ttl, opts)

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.

See Nebulex.Cache.update/4.

Functions

fetch_or_store(adapter_meta, key, fun, ttl, keep_ttl?, opts)

Default implementation for fetch_or_store/6.

get_and_update(adapter_meta, key, fun, ttl, keep_ttl?, opts)

Default implementation for get_and_update/6.

get_or_store(adapter_meta, key, fun, ttl, keep_ttl?, opts)

Default implementation for get_or_store/6.

update(adapter_meta, key, initial, fun, ttl, keep_ttl?, opts)

Default implementation for update/7.