Feel.Worker.Preflight (feel v0.1.0)

Copy Markdown View Source

The budget of the preflight: the step that runs in the caller, before a worker exists, and decides what the job closure copied into the worker holds (the internal run_prepared entry point of Feel.Worker). Internal: not a supported API.

A worker is another process, so what it runs is copied into its heap and counted against :max_input_bytes. A selector (Dmn.Execution.prepare/6 for DMN, the binding selection of Feel.run/3 for FEEL) picks the part of the model and the bindings a call needs, and this module bounds that work, because no worker's deadline or heap policy protects it yet:

  • project/2 gives the selector a state with a step budget of at most 100,000 (less for a small memory or input policy) and the deadline. The deadline is read when the selection starts and then every 64 steps (step/1; a step is constant work, and the transfer walk checks every 256).
  • Only library-owned, shallow work runs here, never host code: no extension, callback or resolver is called.
  • What it cannot decide, or cannot afford (fallback/0: a key that is too long, a spent budget, uncertain metadata), makes it send the original, complete argument instead, which faces the same transfer check. Expiry is a {:error, :timeout} of the whole operation, not a fresh deadline.

Selection is an optimization of what is copied and never validation: the worker validates again. See the preflight in the architecture.

Summary

Types

t()

@type t() :: %{deadline: integer(), left: non_neg_integer()}

Functions

bindings(bindings, names, state)

@spec bindings(map(), MapSet.t() | nil, t()) :: map()

fallback()

@spec fallback() :: no_return()

project(context, select)

@spec project(map(), (t() -> value)) :: {:ok, value} | :fallback | {:error, :timeout}
when value: term()

step(state)

@spec step(t()) :: t()