Supervisor
The Supervisor module provides supervision trees for managing actor lifecycles. Supervisors monitor child actors and can restart them when they stop or terminate, implementing fault-tolerant patterns inspired by Erlang/OTP.
Supervisor operations that create, restart, or monitor actors require ActorAuth,
ConcurrencyAuth, or RootAuth in scope. Derive these tokens through
Auth.Concurrency and pass them into supervisor-facing APIs.
Supervisors form a hierarchy where each supervisor manages its children.
When a child exits, the supervisor's restart strategy determines how to recover.
Use Supervisor.stop-child when you want to remove a child without restarting it.
Restart Strategies
Supervisors support three restart strategies that determine what happens when a child exits:
Child Specifications
Each child is specified as a record with the following fields:
# Child specification record
{
handler: fn(msg, state) => new-state, # Message handler function
state: initial-state, # Initial state for the actor
name: "worker-1", # Optional: registered actor name
max-restarts: 3, # Optional: restarts allowed in the window
restart-window-ms: 5000 # Optional: restart window in milliseconds
}
Named children are registered with the actor registry and can be found with
Actor.lookup. When a named child is restarted, the registration is
refreshed to point at the new actor ID. Restart limits are tracked per child; if
a child exceeds max-restarts inside restart-window-ms,
it is removed from the supervisor instead of being restarted again.
Supervisor API
handler = fn(-msg, state) => state + 1
children = [
{handler: handler, state: 0, name: "worker-1"},
{handler: handler, state: 0, name: "worker-2"}
]
sup = Supervisor.start :isolate children
Supervisor.stop sup
worker = Supervisor.spawn-child sup {
handler: handler,
state: 0,
name: "dynamic-worker"
}
removed? = Supervisor.stop-child sup worker
child-ids = Supervisor.children sup
println "Children: ${child-ids}"
Error Types
type SupervisorError =
| InvalidStrategy {message: NonEmptyString}
| InvalidChildSpec {message: NonEmptyString}
| StartFailed {name: NonEmptyString, message: NonEmptyString}
| NotRunning
| SupervisorError {message: NonEmptyString}
Complete Example
# Define a worker handler
worker-handler = fn(msg, state) =>
match msg
| :work -> state + 1
| :reset -> 0
| _ -> state
# Create child specifications
children = [
{handler: worker-handler, state: 0, name: "worker-1"},
{handler: worker-handler, state: 0, name: "worker-2"},
{handler: worker-handler, state: 0, name: "worker-3"}
]
# Start supervisor with isolate strategy
sup = Supervisor.start :isolate children
# Get child actors and send them work
workers = Supervisor.children sup
workers |> List.each (fn(w) => w <- :work)
# Check status
println "Supervisor running: ${Supervisor.is-running? sup}"
println "Child count: ${Supervisor.child-count sup}"
# Add and remove a child dynamically
dynamic = Supervisor.spawn-child sup {
handler: worker-handler,
state: 0,
name: "dynamic-worker",
max-restarts: 2,
restart-window-ms: 1000
}
Supervisor.stop-child sup dynamic
# Cleanup
Supervisor.stop sup