Jido.Exec (Jido Action v2.0.0)

View Source

Action execution engine with modular architecture for robust action processing.

This module provides the core execution interface for Jido Actions with specialized helper modules handling specific concerns:

  • Jido.Exec.Validator - Parameter and output validation
  • Jido.Exec.Telemetry - Logging and telemetry events
  • Jido.Exec.Retry - Exponential backoff and retry logic
  • Jido.Exec.Compensation - Error handling and compensation
  • Jido.Exec.Async - Asynchronous execution management
  • Jido.Exec.Chain - Sequential action execution
  • Jido.Exec.Closure - Action closures with pre-applied context

Core Features

  • Synchronous and asynchronous action execution
  • Automatic retries with exponential backoff
  • Timeout handling for long-running actions
  • Parameter and context normalization
  • Comprehensive error handling and compensation
  • Telemetry integration for monitoring and tracing
  • Action cancellation and cleanup

Usage

Basic action execution:

Jido.Exec.run(MyAction, %{param1: "value"}, %{context_key: "context_value"})

Asynchronous execution:

async_ref = Jido.Exec.run_async(MyAction, params, context)
# ... do other work ...
result = Jido.Exec.await(async_ref)

See Jido.Action for how to define an Action.

Summary

Functions

Waits for the result of an asynchronous Action execution.

Awaits the completion of an asynchronous Action with a custom timeout.

Cancels a running asynchronous Action execution.

Executes a Action synchronously with the given parameters and context.

Executes a Action asynchronously with the given parameters and context.

Types

action()

@type action() :: module()

async_ref()

@type async_ref() :: %{
  :ref => reference(),
  :pid => pid(),
  optional(:owner) => pid(),
  optional(:monitor_ref) => reference()
}

context()

@type context() :: map()

exec_error()

@type exec_error() :: {:error, Exception.t()}

exec_error_dir()

@type exec_error_dir() :: {:error, Exception.t(), any()}

exec_result()

@type exec_result() ::
  exec_success() | exec_success_dir() | exec_error() | exec_error_dir()

exec_success()

@type exec_success() :: {:ok, map()}

exec_success_dir()

@type exec_success_dir() :: {:ok, map(), any()}

params()

@type params() :: map()

run_opts()

@type run_opts() :: [timeout: non_neg_integer(), jido: atom()]

Functions

await(async_ref)

@spec await(async_ref()) :: exec_result()

Waits for the result of an asynchronous Action execution.

Parameters

  • async_ref: The reference returned by run_async/4.
  • timeout: Maximum time (in ms) to wait for the result (default: 5000).

Returns

  • {:ok, result} if the Action executes successfully.
  • {:error, reason} if an error occurs during execution or if the action times out.
  • {:error, %Jido.Action.Error.InvalidInputError{}} when awaited by a non-owner process.

Examples

iex> async_ref = Jido.Exec.run_async(MyAction, %{input: "value"})
iex> Jido.Exec.await(async_ref, 10_000)
{:ok, %{result: "processed value"}}

iex> async_ref = Jido.Exec.run_async(SlowAction, %{input: "value"})
iex> Jido.Exec.await(async_ref, 100)
{:error, %Jido.Action.Error{type: :timeout, message: "Async action timed out after 100ms"}}

await(async_ref, timeout)

@spec await(async_ref(), timeout()) :: exec_result()

Awaits the completion of an asynchronous Action with a custom timeout.

Parameters

  • async_ref: The async reference returned by run_async/4.
  • timeout: Maximum time to wait in milliseconds.

Returns

  • {:ok, result} if the Action completes successfully.
  • {:error, reason} if an error occurs or timeout is reached.

cancel(async_ref_or_pid)

@spec cancel(async_ref() | pid()) :: :ok | exec_error()

Cancels a running asynchronous Action execution.

Parameters

  • async_ref: The reference returned by run_async/4, or just the PID of the process to cancel.

Returns

  • :ok if the cancellation was successful.
  • {:error, reason} if the cancellation failed or the input was invalid.
  • {:error, %Jido.Action.Error.InvalidInputError{}} when cancelled by a non-owner process.

Examples

iex> async_ref = Jido.Exec.run_async(LongRunningAction, %{input: "value"})
iex> Jido.Exec.cancel(async_ref)
:ok

iex> Jido.Exec.cancel("invalid")
{:error, %Jido.Action.Error{type: :invalid_async_ref, message: "Invalid async ref for cancellation"}}

run(instruction)

@spec run(Jido.Instruction.t()) :: exec_result()

Executes a Action synchronously with the given parameters and context.

Parameters

  • action: The module implementing the Action behavior.
  • params: A map of input parameters for the Action.
  • context: A map providing additional context for the Action execution.
  • opts: Options controlling the execution:
    • :timeout - Maximum time (in ms) allowed for the Action to complete (configurable via :jido_action, :default_timeout).
    • :max_retries - Maximum number of retry attempts (configurable via :jido_action, :default_max_retries).
    • :backoff - Initial backoff time in milliseconds, doubles with each retry (configurable via :jido_action, :default_backoff).
    • :log_level - Override the global Logger level for this specific action. Accepts [:error, :info, :debug, :emergency, :alert, :critical, :warning, :notice].
    • :jido - Optional instance name for isolation. Routes execution through instance-scoped supervisors (e.g., MyApp.Jido.TaskSupervisor).

Action Metadata in Context

The action's metadata (name, description, category, tags, version, etc.) is made available to the action's run/2 function via the context parameter under the :action_metadata key. This allows actions to access their own metadata when needed.

Returns

  • {:ok, result} if the Action executes successfully.
  • {:error, reason} if an error occurs during execution.

Examples

iex> Jido.Exec.run(MyAction, %{input: "value"}, %{user_id: 123})
{:ok, %{result: "processed value"}}

iex> Jido.Exec.run(MyAction, %{invalid: "input"}, %{}, timeout: 1000)
{:error, %Jido.Action.Error{type: :validation_error, message: "Invalid input"}}

iex> Jido.Exec.run(MyAction, %{input: "value"}, %{}, log_level: :debug)
{:ok, %{result: "processed value"}}

# Access action metadata in the action:
# defmodule MyAction do
#   use Jido.Action,
#     name: "my_action",
#     description: "Example action",
#     vsn: "1.0.0"
#
#   def run(_params, context) do
#     metadata = context.action_metadata
#     {:ok, %{name: metadata.name, version: metadata.vsn}}
#   end
# end

run(action, params \\ %{}, context \\ %{}, opts \\ [])

@spec run(action(), params(), context(), run_opts()) :: exec_result()

run_async(action, params \\ %{}, context \\ %{}, opts \\ [])

@spec run_async(action(), params(), context(), run_opts()) :: async_ref()

Executes a Action asynchronously with the given parameters and context.

This function immediately returns a reference that can be used to await the result or cancel the action.

Note: This approach integrates with OTP by spawning tasks under a Task.Supervisor. Make sure {Task.Supervisor, name: Jido.Action.TaskSupervisor} is part of your supervision tree.

Parameters

  • action: The module implementing the Action behavior.
  • params: A map of input parameters for the Action.
  • context: A map providing additional context for the Action execution.
  • opts: Options controlling the execution (same as run/4).

Returns

An async_ref map containing:

  • :ref - A unique reference for this async action.
  • :pid - The PID of the process executing the Action.
  • :owner - The PID of the caller that started the async action.

Examples

iex> async_ref = Jido.Exec.run_async(MyAction, %{input: "value"}, %{user_id: 123})
%{ref: #Reference<0.1234.5678>, pid: #PID<0.234.0>}

iex> result = Jido.Exec.await(async_ref)
{:ok, %{result: "processed value"}}