Sagents.Supervisor (Sagents v0.12.0)

Copy Markdown

Top-level supervisor for Sagents infrastructure.

Starts the process registry, agent dynamic supervisor, and filesystem dynamic supervisor as children under a single supervisor.

Why use this in your application?

OTP shuts down supervision tree children in reverse start order. By adding Sagents.Supervisor to your application's supervision tree after your Repo and PubSub, you ensure that agent processes terminate before Repo and PubSub shut down. This allows agents to persist state and broadcast shutdown events during terminate/2.

Usage

Add Sagents.Supervisor to your application's supervision tree after your Repo, PubSub, and Presence, and before your Endpoint:

# lib/my_app/application.ex
def start(_type, _args) do
  children = [
    MyApp.Repo,
    {Phoenix.PubSub, name: MyApp.PubSub},
    MyAppWeb.Presence,
    Sagents.Supervisor,
    MyAppWeb.Endpoint
  ]

  opts = [strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)
end

The Endpoint must come after Sagents.Supervisor

Reverse shutdown order is the whole point of that placement. Listed after, the Endpoint stops accepting requests first, and the registry is still alive to serve whatever is in flight.

Listed before Sagents.Supervisor, the Endpoint keeps serving requests after the registry is gone, and every one of them fails until the BEAM exits. Correct ordering narrows that window but does not remove it, because the node stays reachable for the platform's whole drain period. Wire Sagents.ready?/0 into your readiness check as well. See docs/deployment.md.

What it starts

The backend (local vs Horde) is determined by application config:

# Single-node (default — no config needed)
config :sagents, :distribution, :local

# Distributed cluster
config :sagents, :distribution, :horde

Restart strategy

Children are supervised :rest_for_one, with the registry first. Everything listed after it depends on it: an AgentSupervisor and an AgentServer register their :via names in the registry once, at start, and nothing re-registers them afterwards. A registry that loses its contents would otherwise leave them running but invisible to every lookup, which lets a conversation acquire a second AgentServer.

:rest_for_one expresses that dependency. A registry failure takes the dynamic supervisors down with it, agents stop, and the next request re-creates them from persisted state. That is a real cost, and it is the right one: agent state is durable, so a restart is recoverable, while a silent duplicate is not.

Sagents.RegistryWatcher sits between the registry and its dependents to connect a registry failure to that chain. On both backends the process that owns the registry's ETS tables runs under a supervisor of the backend's own, which can replace it with a fresh, empty one without ever failing a child of this supervisor. The watcher monitors that table-owning process and fails in its place. See Sagents.ProcessRegistry.watched_name/0 for why it is not always the process registered as Sagents.Registry.

Summary

Functions

Returns a specification to start this module under a supervisor.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

start_link(opts \\ [])