Main facade of the Arrea orchestrator.
Responsible for:
- Parallel execution of tasks and commands
- Worker management via
Arrea.Leader - Fault tolerance with
Arrea.CircuitBreaker - Status reporting via
Arrea.Monitor
Architecture
┌─────────────────────────────────────────────────────────┐
│ Arrea │
│ (Facade) │
└─────────────────────────┬───────────────────────────────┘
│
┌────────────────────────▼───────────────────────────────┐
│ Arrea.Leader (GenServer) │
│ Coordinates execution, manages workers │
│ Emits {:leader_event, event} events │
└─────────────────────────┬───────────────────────────────┘
│
┌────────────────────────▼───────────────────────────────┐
│ Arrea.WorkerSupervisor (DynamicSupervisor) │
│ Ephemeral workers │
└─────────────────────────┬───────────────────────────────┘
│
┌────────────────────────▼───────────────────────────────┐
│ Arrea.Worker (GenServer) │
│ Executes individual tasks │
└─────────────────────────────────────────────────────────┘
Arrea.Monitor — Worker lifecycle statistics
Arrea.CircuitBreaker — Fault toleranceThe supervision tree starts automatically when including Arrea as a
dependency. There is no need to start Arrea.Supervisor manually.
Quick start
# Simple execution
{:ok, result} = Arrea.execute(fn -> :ok end)
# Parallel execution
{:ok, results} = Arrea.run([fn -> 1 end, fn -> 2 end], workers: 4)
# Subscribing to Leader events
:ok = Arrea.subscribe()
receive do
{:leader_event, %{type: :worker_started} = event} ->
IO.inspect(event)
{:leader_event, %{type: :finished} = event} ->
IO.inspect(event)
end
:ok = Arrea.unsubscribe()
Summary
Functions
Executes a single command synchronously.
Returns the maximum number of workers configured.
Executes multiple commands in parallel.
Runs a list of commands or functions in parallel and waits for all to complete.
Returns the current statistics of the Engine.
Subscribes the current process to the Leader's semantic events.
Cancels the subscription of the current process to Leader events.
Types
@type execution_option() :: {:workers, non_neg_integer()} | {:timeout, non_neg_integer()} | {:retry, boolean()} | {:validate, boolean()}
Functions
@spec execute(binary() | (-> term()), [execution_option()]) :: {:ok, Arrea.Result.t()} | {:error, Arrea.Error.t()}
Executes a single command synchronously.
Parameters
cmd— A binary (shell command) or a zero-arity functionopts— Additional options::timeout— Timeout in ms (default30_000). Real timeout: cancels execution.:retry— Whether to retry on failure:shell— Shell to use (highest priority over config and environment):validate— Run safety validation before executing (defaultfalse). Set totrueto reject dangerous commands (:rm -rf,:sudo, etc.) before they run. Disabled by default to preserve prior behaviour; trusted internal callers (e.g. Apero's info commands) can keep the default and pay no per-call cost.
Returns
{:ok, Arrea.Result.t()}— Success with result{:error, Arrea.Error.t()}— Error with code and message
Examples
iex> Arrea.execute("echo hello")
{:ok, %Arrea.Result{success: true, data: %{stdout: "hello\n", ...}, failures: []}}
iex> Arrea.execute(fn -> :work end)
{:ok, %Arrea.Result{success: true, data: :work, failures: []}}
iex> Arrea.execute("rm -rf /tmp", validate: true)
{:error, %Arrea.Error{code: :validation_failed, message: "{:dangerous_command, \"rm -rf\"}"}}
@spec max_workers() :: non_neg_integer()
Returns the maximum number of workers configured.
Example
iex> Arrea.max_workers()
100
@spec run([binary() | (-> term())], [execution_option()]) :: {:ok, Arrea.Result.t()} | {:error, Arrea.Error.t()}
Executes multiple commands in parallel.
Parameters
commands— List of binaries or functionsopts— Options::workers— Maximum number of parallel workers (defaultmax_workers()):timeout— Total timeout in ms
Returns
{:ok, Arrea.Result.t()}— Withbatch_idto correlate events{:error, Arrea.Error.t()}— If all failed or no workers are available
Example
iex> {:ok, result} = Arrea.run([fn -> 1 end, fn -> 2 end, fn -> 3 end], workers: 2)
iex> result.data.batch_id
"batch_..."
Runs a list of commands or functions in parallel and waits for all to complete.
Thin facade over the internal parallel engine so consumers do not need to
reach into Arrea.Parallel (which is marked @moduledoc false).
Options
:workers— Number of parallel workers (default: 4):timeout— Default timeout per command in ms (default: 30_000):ordered— Return results in input order (default: true)
Example
iex> Arrea.run_sync([fn -> 1 end, fn -> 2 end])
[{:ok, %{result: 1, exit_code: 0}}, {:ok, %{result: 2, exit_code: 0}}]
@spec stats() :: {:ok, map()} | {:error, :monitor_unavailable}
Returns the current statistics of the Engine.
The statistics are provided by Arrea.Monitor, which tracks the lifecycle
of all workers started under the Leader.
Example
iex> {:ok, stats} = Arrea.stats()
iex> Map.keys(stats)
[:active_workers, :completed_tasks, :failed_tasks, :total_workers]
@spec subscribe() :: :ok
Subscribes the current process to the Leader's semantic events.
Received messages have the shape {:leader_event, event} where
event is a map with at least the :type key.
Common event types:
%{type: :worker_started, worker_id: id}%{type: :progress, worker_id: id, percent: float, ...}%{type: :finished, worker_id: id}%{type: :error, worker_id: id, reason: term}%{type: :result, worker_id: id, data: term}
To unsubscribe, call unsubscribe/0.
Example
:ok = Arrea.subscribe()
receive do
{:leader_event, %{type: :finished, worker_id: id}} ->
IO.puts("Worker #{id} finished")
end
@spec unsubscribe() :: :ok
Cancels the subscription of the current process to Leader events.
Example
:ok = Arrea.unsubscribe()