StatifierRouter.PinSource (StatifierRouter v0.9.2)

Copy Markdown View Source

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()
end

MyApp.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

__using__(opts)

(macro)

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.

count(config, map)

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.