Baton.Flow.FanOutSpec (Baton v0.27.4)

Copy Markdown View Source

A JSON-compatible fan-out declaration.

When the collection is resolved

collection decides more than which list is read — it decides when the expansion happens, and so which of two mechanisms runs it:

  • rooted at $input. or $context. — a static fan-out. Both roots are populated before anything runs, so Baton.Flow.Compiler expands the node into one job per item inside the workflow's insert transaction.
  • rooted at $steps. — a dynamic fan-out. The collection is an upstream node's result, so its size is unknown until that node finishes. The node compiles to a single expander step that creates the rest at run time (Baton.Expansion).

Everything downstream is identical either way: one job per item, carrying $item, and a reader that declares a dep on the logical node reads the ordered expansion results as $steps.<node_id>.

max_items caps the expansion. It is a guard against a producer that degenerates — the failure mode dynamic fan-out exists to avoid, arriving from the other direction — and applies to both kinds: a static fan-out fails to compile, a dynamic one discards its expander rather than inserting thousands of jobs.

Summary

Functions

The default expansion cap applied when a spec does not set one.

Whether this fan-out is resolved at run time from an upstream result rather than at compile time from the run's input or context.

The step name for one expansion, or why the item cannot have one.

Types

t()

@type t() :: %Baton.Flow.FanOutSpec{
  collection: String.t(),
  gate: String.t(),
  item_id: String.t(),
  max_items: pos_integer()
}

Functions

default_max_items()

@spec default_max_items() :: pos_integer()

The default expansion cap applied when a spec does not set one.

dump(fan_out)

@spec dump(t()) :: map()

dynamic?(fan_out_spec)

@spec dynamic?(t()) :: boolean()

Whether this fan-out is resolved at run time from an upstream result rather than at compile time from the run's input or context.

expansion_id(node_id, suffix)

@spec expansion_id(String.t(), term()) :: {:ok, String.t()} | {:error, term()}

The step name for one expansion, or why the item cannot have one.

Both expanders build the name the same way — the node id, an underscore, and the resolved item_id — and both have the same two ways to fail, so the rule lives here rather than once per path. A suffix that is neither a string nor a number cannot name anything; one that pushes the name past what step_name can hold (Baton.Node.step_name_limit/0) would be refused by the database, and a name refused there surfaces as a raised error from inside the expansion transaction rather than a reason anyone can act on.

Both are permanent properties of the item, so both are for the caller to discard on: the static path cannot compile, and the dynamic path cannot make a model's output shorter by asking again.

gate_atom(fan_out_spec)

@spec gate_atom(t()) :: :parallel | :sequential

The gate as the atom Baton.Expansion and Baton.Flow.FanOut use.

load(attrs)

@spec load(map()) :: {:ok, t()} | {:error, term()}