The address table's vote on a chart retirement:
StatifierPersistence.PinSource implemented over the address rows.
StatifierPersistence.Executions.retire_chart/4 counts the pins it can
see itself and asks the host's pin sources for the ones it cannot. An
address row is one it cannot: it lives in this package's table, in a
package statifier_persistence does not depend on, and it is the reason
a later event still reaches the execution it names (ADR-0002,
section 1).
Most of the time the vote only names the router in the refusal: a row
counted here names an :active execution, and an :active execution on
the hash refuses the retirement on its own. The vote decides the answer
in one window: retire_chart/4 reads the active ids before its
transaction opens, and an execution that goes terminal between that
read and the guarded write inside the transaction no longer refuses on
its own. The count taken here, from the ids read earlier, still does.
What the count is
%{addresses: n}, where n is the number of address rows naming one of
the executions in the context. The context's :execution_ids are the
ids of the :active executions on the content hash, which the retire
call has already read, and that is the only handle this source needs: an
address row carries an execution_id and no content hash, so a source
over this table can answer for a hash it cannot see. The content hash is
therefore not read here.
No horizon is applied to the count. The horizon in
StatifierRouter.Addresses.reap/2 runs from terminal_seen_at, which is
stamped only once this package has seen the row's execution terminal, so
a row naming an :active execution has no horizon running against it
yet. The rows this source counts are live addresses by construction.
So the pin releases when the execution leaves the :active set, not
when its address row is deleted: the next retire call no longer hands
this source that execution's id, and counts one pin fewer, while the
row itself stands until the reaper stamps it terminal and deletes it
once the horizon has elapsed. Nothing here retains a row, and nothing
here deletes one.
How a host installs it
The behaviour's callback takes a content hash and a context and nothing
else, so the host binds the configuration this source reads the table
through in a module of its own. use StatifierRouter.PinSource is how
this package spells that module, with :config an expression it
evaluates on every call:
defmodule MyApp.RouterPins do
use StatifierRouter.PinSource, config: MyApp.Router.config()
endMyApp.Router.config/0 is the host's own: the same
%StatifierRouter.Config{} it routes events with. The host then names
the module at the retire call:
StatifierPersistence.Executions.retire_chart(store, content_hash, [MyApp.RouterPins])Nothing forces the macro. A host that would rather not use it writes
the same module by hand, with @behaviour StatifierPersistence.PinSource
and a pins/2 that calls count/2 with its own configuration, and it
works the same.
StatifierRouter.PinSource itself is not a pin source: it defines no
pins/2. Naming it at the retire call retires nothing and refuses with
the source-failure arm:
{:error,
{:pin_source_failed,
{StatifierRouter.PinSource, {:raised, %UndefinedFunctionError{}}}}}The module to name is the one the host wrote.
A source that cannot answer raises
StatifierPersistence.PinSource makes raising the way to say "I could
not answer", because a source that answers zero when it does not know
retires a pinned chart. This one keeps that: a context without
:execution_ids, or a configuration that is not a
%StatifierRouter.Config{}, raises rather than counting nothing, and
StatifierPersistence.PinSource.collect/3 turns the raise into
{:error, {module, {:raised, exception}}}. A read that the repo itself
refuses raises for the same reason and reaches the caller the same way.
Summary
Functions
Writes a StatifierPersistence.PinSource over the address table.
The address rows under config naming one of the executions in
context, as %{addresses: n}.
Functions
Writes a StatifierPersistence.PinSource over the address table.
Takes one option, :config, an expression evaluating to the
%StatifierRouter.Config{} whose repo and table the rows are counted
in. It is evaluated on every call, so a host whose configuration is
built at run time passes the call that builds it.
@spec count(StatifierRouter.Config.t(), StatifierPersistence.PinSource.context()) :: %{ addresses: non_neg_integer() }
The address rows under config naming one of the executions in
context, as %{addresses: n}.
This is what a module written by use StatifierRouter.PinSource answers
with. It raises for a context carrying no :execution_ids, which is the
refusal StatifierPersistence.PinSource asks a source for.