The determinism seam (implementation.md §10). Everything nondeterministic — time, randomness, cross-node communication, and sim-visible progress marks — goes through this one module, so a test harness can take it over. Together the seams support a protocol-level deterministic simulation harness (FoundationDB/TigerBeetle-style): one scheduler owning one event queue.
Compile-switched: prod builds bind Fief.Seam.Real with zero indirection;
test builds bind Fief.Seam.Sim, which falls through to Real unless the
calling process has hooked a simulation (Fief.Sim). One compile key,
config :fief, :seam, selects the implementation for the function seams; the
checkpoint/2 macro is a separate boolean switch (:fief, :trace_checkpoints).
The function seams stay real function calls dispatched through a bound
@impl_mod, not macro-expanded raw calls — deliberately: send/3,
send_after/2, and the rest remain, on Fief.Seam.Real, single traceable prod choke points, so
every cross-node send and every timer arm is one MFA you can :dbg/recon-trace.
Time
Every clock read and every deadline in fief goes through this module:
lease basis, fence timer, planner cadence, pull timeouts, sweep ticks, poll
intervals. Raw timers (Process.send_after, :erlang.start_timer, gen_statem
state timeouts, receive-after) are banned outside the seam modules and enforced
by scripts/check_seams.sh.
Timer semantics follow :erlang.start_timer/3: send_after/2 arms a timer
that delivers {:timeout, ref, msg} to the calling process, where ref is
the returned reference. All times are milliseconds.
Randomness
Every jitter and random choice goes through this module: :moved-fallback
jitter, leader-poll jitter. Direct :rand. calls outside the seam modules are
banned and enforced by scripts/check_seams.sh. The sim implementation is
seedable per-process for deterministic tests and falls through to real
otherwise.
Transport
vnode-name minting and the dist send (implementation.md §5). Every cross-node
send goes through here; the real implementation is
:erlang.send({name, node}, msg, [:noconnect]) — a :noconnect return is a
routing failure for the caller to handle, never a blocking auto-connect.
Inbound addressing uses per-vnode registered names namespaced by instance — a bounded atom set (instances × partitions), minted at instance boot.
Checkpoints
The scheduler controls what crosses its queue — seam timers, transport
deliveries, arbiter ops — but intra-node execution between those points is
invisible to it. checkpoint/2 marks such points: places where the harness
needs to observe progress it cannot infer from the queue, the load-bearing
case being quiescence ("this delivery has been fully processed, virtual time
may advance").
Fief.Seam.checkpoint(:renewed, node: node_id)Unlike the function seams this is a boolean compile switch, not a module
binding: in prod builds the macro expands to :ok — no call, no argument
evaluation, nothing to keep off the hot path. In test builds it expands to
an emit through Fief.Seam.Sim, which sends
{:fief_checkpoint, tag, meta, pid} to the tracer bound to the calling
process (Fief.Sim.bind_tracer/1) and no-ops when unhooked — real-unless-
hooked, like every other seam.
Because prod builds discard arguments unevaluated, checkpoint args must be cheap and side-effect-free: literal tags, existing bindings. Never compute something only for a checkpoint.
Summary
Functions
Cancel a timer and consume its {:timeout, ref, msg} if it already fired
into the caller's mailbox — the cancel-after-due stale-delivery case for
code that runs in arbitrary caller processes (Fief.Router), where the
gen_statem stale-timer catch-all convention (M3) is not available and a
leaked timeout message would pollute the caller's mailbox. Non-blocking:
a fired-but-not-yet-delivered timer (possible under simulation, where a due
timer is a pending scheduler event) is simply not there to flush, matching
:erlang.cancel_timer/1 semantics.
Cancel a timer armed with send_after/2. Returns remaining ms or false.
Mark a sim-visible progress point (see the "Checkpoints" section above). In
prod builds this expands to :ok with tag/meta discarded unevaluated; in
test builds it emits through Fief.Seam.Sim.emit/2.
Monotonic milliseconds. The only clock fief code may read.
Mint the registered name of instance's planner channel — the cross-node
address settle reports are relayed to (M4 phase B). One atom per instance;
the leading node's Fief.Planner registers it while leading, so a send to
{planner_name, leader_node} reaches the planner or drops silently like any
other unregistered name.
Send msg to the process registered as name on node, without connecting.
{:error, :noconnect} means no live dist connection — a routing failure to
refresh-and-retry, per implementation.md §5. A send to an unregistered
name (local or remote) is silently dropped, exactly like dist: :ok means
handed off, never delivered — non-delivery surfaces as the caller's reply
timeout.
Arm a timer delivering {:timeout, ref, msg} to self() after ms.
Uniform float in [0.0, 1.0).
Uniform integer in 1..n.
Mint the registered name for vnode_id of instance. Bounded atom set.
Types
@type timer_ref() :: reference()
Callbacks
@callback cancel_timer(timer_ref()) :: non_neg_integer() | false
@callback monotonic_time() :: integer()
@callback send_after(ms :: non_neg_integer(), msg :: term()) :: timer_ref()
@callback uniform() :: float()
@callback uniform(n :: pos_integer()) :: pos_integer()
@callback vnode_name(instance :: atom(), vnode_id :: non_neg_integer()) :: atom()
Functions
@spec cancel_and_flush(timer_ref()) :: :ok
Cancel a timer and consume its {:timeout, ref, msg} if it already fired
into the caller's mailbox — the cancel-after-due stale-delivery case for
code that runs in arbitrary caller processes (Fief.Router), where the
gen_statem stale-timer catch-all convention (M3) is not available and a
leaked timeout message would pollute the caller's mailbox. Non-blocking:
a fired-but-not-yet-delivered timer (possible under simulation, where a due
timer is a pending scheduler event) is simply not there to flush, matching
:erlang.cancel_timer/1 semantics.
@spec cancel_timer(timer_ref()) :: non_neg_integer() | false
Cancel a timer armed with send_after/2. Returns remaining ms or false.
Mark a sim-visible progress point (see the "Checkpoints" section above). In
prod builds this expands to :ok with tag/meta discarded unevaluated; in
test builds it emits through Fief.Seam.Sim.emit/2.
@spec monotonic_time() :: integer()
Monotonic milliseconds. The only clock fief code may read.
Mint the registered name of instance's planner channel — the cross-node
address settle reports are relayed to (M4 phase B). One atom per instance;
the leading node's Fief.Planner registers it while leading, so a send to
{planner_name, leader_node} reaches the planner or drops silently like any
other unregistered name.
Send msg to the process registered as name on node, without connecting.
{:error, :noconnect} means no live dist connection — a routing failure to
refresh-and-retry, per implementation.md §5. A send to an unregistered
name (local or remote) is silently dropped, exactly like dist: :ok means
handed off, never delivered — non-delivery surfaces as the caller's reply
timeout.
@spec send_after(non_neg_integer(), term()) :: timer_ref()
Arm a timer delivering {:timeout, ref, msg} to self() after ms.
@spec uniform() :: float()
Uniform float in [0.0, 1.0).
@spec uniform(pos_integer()) :: pos_integer()
Uniform integer in 1..n.
@spec vnode_name(atom(), non_neg_integer()) :: atom()
Mint the registered name for vnode_id of instance. Bounded atom set.