Arrea.LongRunning (Arrea v2.2.0)

Copy Markdown View Source

Supervisor-backed wrapper around Port.open/2 for long-running OS processes.

Use this when you need to spawn a binary that stays alive for the lifetime of the VM (or a long batch) and want:

  • Registration in Arrea.Registry for lookup/health/stop
  • Telemetry events on lifecycle ([:arrea, :long_running, ...])
  • Automatic cleanup via DynamicSupervisor (Arrea.WorkerSupervisor)
  • Crash propagation back to the caller

Designed for processes like llama-server (LLM inference), Postgres sidecars, message brokers, dev databases — anything that you'd otherwise spawn with raw Port.open and reinvent the supervision boilerplate for.

Usage

{:ok, pid} = Arrea.LongRunning.start_link(
  id: :llama_for_llama3,
  binary: "/usr/local/bin/llama-server",
  args: ["--model", "llama-3.gguf", "--port", "8080"],
  health: fn pid -> GenServer.call(pid, :health) end
)

:ok = Arrea.LongRunning.stop(:llama_for_llama3)

Telemetry

Emits:

  • [:arrea, :long_running, :started]id, binary, pid
  • [:arrea, :long_running, :stopped]id, exit_code
  • [:arrea, :long_running, :data]id, data (stdout/stderr)
  • [:arrea, :long_running, :crashed]id, reason

Health checks

Pass a :health option (zero-arity function) when starting. It runs after the :started event so callers can do a readiness probe (e.g. HTTP GET against the spawned process). health/1 returns:

  • :ok — health probe returned truthy
  • {:error, reason} — probe returned falsy or raised
  • :not_found — no process registered with that id

Summary

Functions

Returns a specification to start this module under a supervisor.

Runs the configured health probe for the given id.

Starts a long-running process under Arrea.WorkerSupervisor.

Starts a long-running process synchronously (linked to the caller).

Returns a snapshot of the current state for the given id.

Stops a long-running process by id (graceful: closes the port).

Writes data to the process stdin. Useful for inter-process protocols.

Types

id()

@type id() :: term()

opt()

@type opt() ::
  {:id, id()}
  | {:binary, Path.t()}
  | {:args, [String.t()]}
  | {:env, [{String.t(), String.t()}]}
  | {:cd, String.t()}
  | {:health, (-> any())}
  | {:name, GenServer.name()}

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

health(id)

@spec health(id()) :: :ok | {:error, term()} | :not_found

Runs the configured health probe for the given id.

start(opts)

Starts a long-running process under Arrea.WorkerSupervisor.

Returns {:ok, pid} (like DynamicSupervisor.start_child/2).

start_link(opts)

@spec start_link([opt()]) :: GenServer.on_start()

Starts a long-running process synchronously (linked to the caller).

See moduledoc for options.

state(id)

@spec state(id()) :: {:ok, map()} | {:error, :not_found}

Returns a snapshot of the current state for the given id.

stop(id)

@spec stop(id()) :: :ok | {:error, :not_found}

Stops a long-running process by id (graceful: closes the port).

write(id, data)

@spec write(id(), iodata()) :: :ok | {:error, term()}

Writes data to the process stdin. Useful for inter-process protocols.

Only works if the underlying port was opened with :binary (which is the default in start_link/1).