Consistent vnode ownership for Elixir clusters.

Fief is instance-based: nothing runs under the :fief application itself. Start instances in your own supervision tree, Ecto/Oban-style:

children = [
  {Fief,
   name: MyApp.Fief,
   authority: {Fief.Authority.Local, []},
   partitions: 64}
]

The instance :name is the namespace — the identity of the coordination domain, which must match across every node participating in this instance. Multiple instances coexist in one BEAM and on one database.

See docs/design.md for guarantees and protocols, docs/implementation.md for the code shape this module tree follows.

Summary

Functions

Optional instance-module sugar (implementation.md §9, deferred from M4 to M6): defmodule MyApp.Fief do use Fief, partitions: 64, ... end generates a module-named instance — child_spec/1 (runtime opts override use-time opts; :name is always the module) plus call/3, cast/2, owner_of/1, table/0, epoch/0, leader/0 wrapping the instance-first API. The core stays plain opts; this is convenience only.

Call vnode of instance with msg and await the impl's reply: {:ok, reply} or {:error, reason}. Runs in the caller (no router process); opts takes :timeout (ms), covering the entire :moved-retry loop. See Fief.Router.call/4.

Fire-and-forget send to vnode's owner. See Fief.Router.cast/3.

The epoch the cached table was read at. Hint-grade, an ETS read.

The current planner leader, {:ok, {node_id, term}}, answered from the Authority through the instance's Leadership adapter. {:error, :disabled} when the instance runs no leadership (leadership: false or no vnode impl).

The presumed owner of vnode — hint-grade. See Fief.Router.owner_of/2.

Start a Fief instance. Prefer {Fief, opts} in a supervision tree.

The cached vnode table — {:ok, %{vnode => {owner, prev_owner, row_epoch}}, epoch} — hint-grade, from this node's routing cache (an ETS read; the Authority is the truth).

Functions

__using__(use_opts)

(macro)

Optional instance-module sugar (implementation.md §9, deferred from M4 to M6): defmodule MyApp.Fief do use Fief, partitions: 64, ... end generates a module-named instance — child_spec/1 (runtime opts override use-time opts; :name is always the module) plus call/3, cast/2, owner_of/1, table/0, epoch/0, leader/0 wrapping the instance-first API. The core stays plain opts; this is convenience only.

call(instance, vnode, msg, opts \\ [])

@spec call(atom(), non_neg_integer(), term(), keyword()) ::
  {:ok, term()} | {:error, Fief.Router.call_error()}

Call vnode of instance with msg and await the impl's reply: {:ok, reply} or {:error, reason}. Runs in the caller (no router process); opts takes :timeout (ms), covering the entire :moved-retry loop. See Fief.Router.call/4.

cast(instance, vnode, msg)

@spec cast(atom(), non_neg_integer(), term()) ::
  :ok | {:error, Fief.Router.call_error()}

Fire-and-forget send to vnode's owner. See Fief.Router.cast/3.

epoch(instance)

@spec epoch(atom()) :: {:ok, non_neg_integer()} | {:error, :not_ready | :not_running}

The epoch the cached table was read at. Hint-grade, an ETS read.

leader(instance)

@spec leader(atom()) ::
  {:ok, {term(), pos_integer()}}
  | {:error, :none | :unreachable | :disabled | :not_running}

The current planner leader, {:ok, {node_id, term}}, answered from the Authority through the instance's Leadership adapter. {:error, :disabled} when the instance runs no leadership (leadership: false or no vnode impl).

owner_of(instance, vnode)

@spec owner_of(atom(), non_neg_integer()) ::
  {:ok, term()} | {:error, :no_owner | :unreachable | :not_running}

The presumed owner of vnode — hint-grade. See Fief.Router.owner_of/2.

start_link(opts)

@spec start_link(keyword()) :: Supervisor.on_start()

Start a Fief instance. Prefer {Fief, opts} in a supervision tree.

table(instance)

@spec table(atom()) ::
  {:ok, map(), non_neg_integer()} | {:error, :not_ready | :not_running}

The cached vnode table — {:ok, %{vnode => {owner, prev_owner, row_epoch}}, epoch} — hint-grade, from this node's routing cache (an ETS read; the Authority is the truth).