The admission owner (FX-001): one process outside the replaceable stage subtree, holding its pipeline generation's admitted-work ledger.
The contract, in brief — a submission is one transaction: the owner
reserves the permit AND forwards the packet (submit/3), so no caller
death can strand a reservation without its packet. A submission either
acquires a permit (the work is accounted from there) or meets a
definite refusal — every definite refusal is enforced again at
dequeue. The one thing that can still execute after its caller heard
otherwise is a submission admitted in the last instant before its
acknowledgment was lost, and that outcome carries its own name —
{:unacknowledged, ref} — never the name of a refusal. Permits are
held for the work's whole lifetime: a caller's timeout or death
releases nothing, and a topology failure RETAINS its generation's
permits — surviving work keeps executing and keeps its capacity until
its own terminal release, while work destroyed with the topology
stays admitted, outcome unknown. Nothing is invented into success or
failure, and no slot is reused under surviving work. Every admitted
job is either released to exactly one terminal outcome or remains an
unresolved reservation — reported active, outcome unknown — until
the pipeline is restarted (retention with manual recovery: stop and
start again, a confirmed termination of the whole execution
generation). See docs/research/flowex/C-admission-design.md.
The owner monitors every line worker. Any worker death quiesces the
generation; because the wrapper supervisor is :rest_for_one with the
owner FIRST, the owner's own death restarts the whole line too — a
lost ledger can never overlap still-executing old work.
Summary
Functions
Returns a specification to start this module under a supervisor.
Releases a permit with its terminal outcome — exactly once, by ref, in any generation: a job admitted before a topology failure still holds its permit while it survives, and this is how it frees it. A second notice, or one for an unknown ref, is refused as stale.
The generation's ledger, for diagnostics and reconciliation tests.
active counts unresolved reservations — queued and executing work,
and work destroyed with the topology whose outcome is unknown until
the pipeline is restarted; refs exposes those reservations'
identities. refs is unresolved-reservation VISIBILITY, not admission
history: a completed request leaves the list, so a ref's absence means
released-or-never-admitted and does not prove non-admission. The
ledger keeps no record of past admissions.
Submits ip as one transaction: the permit is reserved and the packet
forwarded to the pipeline's ingress inside the owner, so the reservation
can never exist without its packet. Returns the consumer incarnation the
packet was forwarded to — one incarnation for the caller's monitor and
for the submission itself.
Types
Functions
@spec admit(GenServer.name(), reference()) :: {:ok, integer()} | {:error, :overloaded | :unavailable | :noprocess} | {:error, {:unacknowledged, nil}}
Reserves a permit for id without a packet — a diagnostic for ledger
and reconciliation probes; the engine paths use submit/3. Refusal
semantics as submit/3 with the settling grace as the whole budget;
an unacknowledged diagnostic attempt reports {:error, {:unacknowledged, nil}}.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec release(GenServer.name(), reference(), outcome()) :: :ok | {:error, :stale}
Releases a permit with its terminal outcome — exactly once, by ref, in any generation: a job admitted before a topology failure still holds its permit while it survives, and this is how it frees it. A second notice, or one for an unknown ref, is refused as stale.
@spec report(GenServer.name()) :: %{ generation: integer(), capacity: pos_integer(), active: non_neg_integer(), refs: [reference()], status: :open | :settling, counts: %{optional(outcome()) => non_neg_integer()} }
The generation's ledger, for diagnostics and reconciliation tests.
active counts unresolved reservations — queued and executing work,
and work destroyed with the topology whose outcome is unknown until
the pipeline is restarted; refs exposes those reservations'
identities. refs is unresolved-reservation VISIBILITY, not admission
history: a completed request leaves the list, so a ref's absence means
released-or-never-admitted and does not prove non-admission. The
ledger keeps no record of past admissions.
@spec start_link(pos_integer(), [term()], {term(), term()} | nil, GenServer.name()) :: GenServer.on_start()
@spec submit(GenServer.name(), FlowExtra.IP.t(), integer() | nil) :: {:ok, pid()} | {:error, :overloaded | :unavailable | :deadline | :noprocess} | {:error, {:unacknowledged, reference()}}
Submits ip as one transaction: the permit is reserved and the packet
forwarded to the pipeline's ingress inside the owner, so the reservation
can never exist without its packet. Returns the consumer incarnation the
packet was forwarded to — one incarnation for the caller's monitor and
for the submission itself.
While the topology settles, the attempt retries against ONE fixed
budget: the caller's deadline when given (the call's clock is the
admission's clock), capped by the settling grace. That budget travels
IN the submission envelope and is checked at dequeue — a submission the
caller has already been refused for (its budget expired waiting for the
acknowledgment) is refused again by the owner, never forwarded, never
executed after the fact.
Refusal certainty — the outcome names what the caller actually knows:
{:error, :overloaded}— definite: at capacity, nothing reserved, nothing will execute.{:error, :unavailable}— definite: the owner itself refused — settling, or the attempt's deadline expired at dequeue — nothing reserved, nothing will execute.{:error, {:unacknowledged, ref}}— UNKNOWN: the acknowledgment timed out and the submission may have been admitted in the last instant before the reply was lost. Keep therefand inspectreport/1's:refs— unresolved-reservation visibility, not admission history: a ref present means admitted-and-unresolved; a ref ABSENT means released or never admitted, and does not prove non-admission.{:error, :deadline}— the caller's own budget ran out; the residual uncertainty is the ordinary timeout contract (execution may continue past a caller's deadline).{:error, :noprocess}— communication with the admission owner failed because the process was unavailable or terminated. This result does not establish whether the submission executed or produced effects before that failure. It is not a definite admission refusal.