Runic.Workflow.SchedulerPolicy (Runic v0.1.0-alpha.7)

Copy Markdown View Source

Defines per-node scheduling policies for workflow execution.

A SchedulerPolicy controls retry behavior, timeouts, failure handling, execution mode, and other scheduling concerns for individual workflow nodes.

Policies are resolved at runtime by matching against runnable nodes using a list of {matcher, policy_map} tuples. The first matching rule wins, and its policy map is merged over the default policy.

Matcher Types

  • atom() — exact match on the node's name
  • :default — catch-all, always matches
  • {:name, %Regex{}} — regex match on the node's name
  • {:type, module} — match on the node's struct module
  • {:type, [modules]} — match on any of the listed struct modules
  • fn/1 — custom predicate function receiving the node

Backoff Strategies

  • :none — no delay between retries
  • :linearmin(base_delay_ms * (attempt + 1), max_delay_ms)
  • :exponentialmin(base_delay_ms * 2^attempt, max_delay_ms)
  • :jitter — randomized exponential: min(rand(base_delay_ms * 2^attempt), max_delay_ms)

Execution Modes

  • :sync — standard synchronous execution (default)
  • :async — asynchronous execution within the workflow's react cycle
  • :durable — enables event emission (%RunnableDispatched{}, %RunnableCompleted{}, %RunnableFailed{}) for crash recovery and audit trails. Used by Runic.Runner.Worker to persist runnable lifecycle events in the workflow log.

Fallback Functions

When all retries are exhausted and a fallback function is set, it receives (runnable, error) and must return one of:

  • %Runnable{} — a modified runnable to execute once (no further retries)
  • {:retry_with, %{key: value}} — overrides merged into meta_context, then executed once
  • {:value, term} — a synthetic value used as the step's output

Any other return value causes the runnable to fail with {:invalid_fallback_return, value}.

Example

alias Runic.Workflow.SchedulerPolicy

policies = [
  {:call_llm, %{max_retries: 3, backoff: :exponential, timeout_ms: 30_000}},
  {{:type, Runic.Workflow.Step}, %{max_retries: 1, backoff: :linear}},
  {:default, %{timeout_ms: 10_000}}
]

policy = SchedulerPolicy.resolve(runnable, policies)

Summary

Functions

Returns the default policy struct.

Policy preset for fast-fail scenarios: no retries, 5s timeout, halt on failure.

Policy preset for I/O-bound operations (HTTP, database, file system).

Policy preset for LLM / external AI model calls.

Merges two policy maps, with overrides taking precedence over base.

Merges runtime override policies with workflow base policies.

Creates a new SchedulerPolicy from a map or keyword list.

Resolves a SchedulerPolicy for a given runnable by walking a list of {matcher, policy_map} tuples top-to-bottom. First match wins.

Types

fallback_fn()

@type fallback_fn() ::
  (Runic.Workflow.Runnable.t(), term() -> fallback_return()) | nil

fallback_return()

@type fallback_return() ::
  Runic.Workflow.Runnable.t() | {:retry_with, map()} | {:value, term()}

t()

@type t() :: %Runic.Workflow.SchedulerPolicy{
  backoff: :none | :linear | :exponential | :jitter,
  base_delay_ms: non_neg_integer(),
  circuit_breaker: map() | nil,
  deadline_ms: non_neg_integer() | nil,
  execution_mode: :sync | :async | :durable,
  executor: module() | :inline | nil,
  executor_opts: keyword(),
  fallback: fallback_fn(),
  idempotency_key: term() | nil,
  max_delay_ms: non_neg_integer(),
  max_retries: non_neg_integer(),
  on_failure: :halt | :skip,
  priority: :low | :normal | :high | :critical,
  timeout_ms: non_neg_integer() | :infinity
}

Functions

default_policy()

@spec default_policy() :: t()

Returns the default policy struct.

fast_fail()

@spec fast_fail() :: t()

Policy preset for fast-fail scenarios: no retries, 5s timeout, halt on failure.

io_policy(opts \\ [])

@spec io_policy(keyword()) :: t()

Policy preset for I/O-bound operations (HTTP, database, file system).

Defaults: 2 retries, linear backoff (500ms base), 10s timeout, skip on failure. Override any default via opts.

llm_policy(opts \\ [])

@spec llm_policy(keyword()) :: t()

Policy preset for LLM / external AI model calls.

Defaults: 3 retries, exponential backoff (1s base, 30s max), 30s timeout, halt on failure. Override any default via opts.

merge(base, overrides)

@spec merge(map(), map()) :: map()

Merges two policy maps, with overrides taking precedence over base.

merge_policies(runtime_overrides, workflow_base)

@spec merge_policies(list() | nil, list()) :: list()

Merges runtime override policies with workflow base policies.

In :merge mode (default), runtime overrides are prepended to the workflow base. In :replace mode, only the runtime overrides are returned.

When runtime_overrides is nil or [], returns workflow_base unchanged.

merge_policies(overrides, workflow_base, arg3)

@spec merge_policies(list() | nil, list(), :merge | :replace) :: list()

new(opts)

@spec new(map() | keyword()) :: t()

Creates a new SchedulerPolicy from a map or keyword list.

Raises ArgumentError if any unknown keys are provided.

resolve(runnable, policies)

@spec resolve(Runic.Workflow.Runnable.t(), list() | nil) :: t()

Resolves a SchedulerPolicy for a given runnable by walking a list of {matcher, policy_map} tuples top-to-bottom. First match wins.

The matched policy map is merged over the default policy. If no match is found or policies is nil or [], returns the default policy.