For anyone evaluating whether Fief is the right tool — no Fief knowledge assumed.
Fief assigns ownership of keys to nodes in an Elixir cluster, with strong consistency: at most one node owns a given key at any instant, in every failure mode. That sentence is unconditional for message routing, and holds for live processes and their side effects under the default fencing mode; the exact contract, scope conditions included, is one page away on guarantees.
Two first-class surfaces build on that ownership. Fief.Key backs each
key with a process — lifecycle callbacks, state that follows ownership,
fencing policy — and is the surface most of this documentation tours.
Fief.Cache is a partitioned, single-owner cache: plain get/put/
delete, where the ownership machinery buys single-writer-per-key coherence
instead of process lifecycles (using Fief.Cache). An instance
runs exactly one surface; to run both, run two instances.
It is arbiter-based: a small linearizable store — Postgres, in the shipped adapter — holds the authoritative coordination state, and every node derives its behavior from it. When forced to choose, Fief chooses unavailability over inconsistency.
The gap it fills
The Elixir ecosystem is rich in AP (available, eventually consistent)
distribution primitives — Horde, Swarm, syn, pg — and nearly empty of CP
ones. The AP tools are CRDT- or gossip-based: during a netsplit, both sides
of the partition can register the same key, and the conflict is resolved
after the damage is done. :global is stronger within a connected cluster
but resolves post-split name conflicts by killing one process at random. The
one real CP building block, ra (Raft), is a library to build on, not a
product to use.
Fief is, roughly, "Horde, but CP": a registry and ownership layer where exactly one node owns a key at any moment, at the cost of availability during failures — and it leans on infrastructure most teams already run (Postgres) instead of asking them to operate a consensus cluster.
How it works, in four sentences
Keys hash to a fixed number of vnodes (partitions), and the only shared
state is the small vnode→node assignment table — a few kilobytes regardless
of key count — held in the arbiter, versioned by a monotonic epoch, mutated
only through compare-and-swap. Every node holds a TTL lease it must keep
renewing; a node that cannot prove its lease is current stops serving
itself, unilaterally, before anyone else is allowed to take over
(leases). Per-key state is never replicated or agreed
upon — it lives only on the owning node, and when ownership moves it either
follows through your own serialize/rehydrate callbacks (Fief.Key,
the key lifecycle) or is dropped and rebuilt from misses
(Fief.Cache). That is why Fief scales in keys (millions) rather than
nodes.
When to use it
Use Fief when a doubly-executed key is expensive or dangerous: exclusive
side effects (billing, ledger postings, outbound API calls that must not
race themselves), singleton workers per entity, per-key state machines whose
invariants break under concurrent owners (Fief.Key) — or a cache where two
nodes writing the same key behind each other's backs is a correctness bug,
not a nuisance (Fief.Cache). The trade you accept in return: during
failures, keys go unavailable — for at least a lease TTL — instead of ever
being served twice.
When not to use it
(This section is advice.)
- When AP is fine — which is most of the time. Presence, pub/sub
fan-out, best-effort caches, anything where a brief double-registration is
an annoyance rather than a correctness bug: Horde or syn are simpler and
stay available through partitions. (A cache is the canonical example in
both directions: if occasionally-stale entries are fine, a plain AP cache
beats
Fief.Cache; reach forFief.Cacheonly when you need at most one writer per key.) Reach for CP because you need it, not because it sounds safer. - When you need delivery guarantees. Fief has none — a node crash takes mailboxes with it, and a timed-out call has an unknown outcome. No clustering layer can give you at-least-once processing; you build acks and retries above it either way (delivery and errors).
- When the cluster is large. Fief assumes a full distribution mesh and targets tens of nodes, not hundreds. Millions of keys, a modest number of nodes.
- When Postgres as a single point of failure is unacceptable. With the arbiter down, membership cannot change and (by default) nodes freeze within a lease TTL. The authority contracts are pluggable precisely so a consensus-backed store can replace Postgres later, but v1 ships Postgres and treats it as an accepted SPOF.
Non-goals, up front
No delivery guarantees; no per-sender message ordering across an ownership transfer; no large-cluster ambitions; no HA arbiter in v1. Each of these is deliberate, and the reasoning lives in the design notes below.
Where to go next
- Guarantees — the exact contract: the four guarantees, their scope conditions, and what is never promised. If you read one page to decide on Fief, read that one.
- Getting started — deps to first
call/3round-trip. - The key lifecycle — the callbacks you implement and when each runs.
- Using Fief.Cache — the cache surface: coherent
get/put/deletewithout the process machinery.
Design notes: docs/design.md §1 (motivation), §3 (non-goals), §4 (core
design decisions).