NAME
Acme::Parataxis::Supervisor - OTP-Style Restart Supervision for Actors and Nested Subtrees
SYNOPSIS
use v5.40;
use Acme::Parataxis qw[async fiber await_sleep];
use Acme::Parataxis::Actor;
use Acme::Parataxis::Supervisor;
async {
my $sup = Acme::Parataxis::Supervisor->new(
strategy => 'OneForOne', # OneForOne | OneForAll | RestForOne
max_restarts => 3,
within => 30, # seconds
);
# Supervise an actor factory:
$sup->supervise(
sub {
Acme::Parataxis::Actor->spawn(
sub ( $self, $msg ) {
die "Crash requested\n" if $msg eq 'crash';
return "Handled: $msg";
},
16,
supervised => 1 # supervised => 1 is required for supervision!
);
},
name => 'worker'
);
fiber {
await_sleep(20);
say $sup->child('worker')->ask('hello')->await; # Handled: hello
# Provoke a crash; the supervisor transparently replaces the worker:
$sup->child('worker')->send('crash');
await_sleep(20) until $sup->restarts('worker') > 0;
say "Restarts observed: " . $sup->restarts('worker');
say $sup->child('worker')->ask('ping')->await; # Handled: ping
$sup->stop;
};
$sup->run; # supervises workers until stop() or restart budget exhaustion
};
DESCRIPTION
While structured concurrency (via Acme::Parataxis::Nursery) provides fail-fast semantics by tearing down a group of fibers on the first error, an Acme::Parataxis::Supervisor implements the Erlang/OTP heal-fast pattern. It runs a supervision fiber that monitors long-lived child workers and automatically restarts them according to a predefined restart strategy when they fail.
Supervised children can be Acme::Parataxis::Actors or nested Acme::Parataxis::Supervisor instances.
Restart Strategies
OneForOne (default): When a child dies, only that specific child is restarted. Sibling workers continue running uninterrupted.
OneForAll: When any child dies, all supervised children are stopped and restarted. Use this when workers share in-memory state that could be corrupted by a sibling crash.
RestForOne: When a child dies, that child and any siblings started after it in registration order are restarted. Earlier workers are left alone.
The Restart Budget
To prevent infinite crash loops when a resource or dependency is permanently broken, restarts are rate-limited: if more than max_restarts occur within within seconds, the supervisor gives up, shuts down all surviving children, and croaks out of run() with an Acme::Parataxis::Error::Supervisor exception.
METHODS
new( [%opts] )
my $sup = Acme::Parataxis::Supervisor->new(
strategy => 'OneForOne',
max_restarts => 5,
within => 60,
);
Constructs a new supervisor.
strategy:'OneForOne','OneForAll', or'RestForOne'(default'OneForOne').max_restarts: Maximum permitted restarts inside the tracking window before the supervisor aborts (default5).within: Sliding time window in seconds (default60). Set to0for an infinite budget that never trips.
supervise( $factory_or_child, [name => $name] )
$sup->supervise( sub {
Acme::Parataxis::Actor->spawn( $handler, 16, supervised => 1 );
}, name => 'worker' );
Registers a child under supervision. Returns $self to support method chaining. Must be called before run().
$factory_or_child should ideally be a CODE reference factory returning an Acme::Parataxis::Actor or a nested Supervisor. A factory allows the supervisor to construct a completely fresh instance (with a clean mailbox) upon each restart.
name must be unique within the supervisor; if omitted, defaults to child_1, child_2, etc.
run( )
$sup->run;
Starts the tree, spawns all initial children, and supervises them until stop() is invoked or the restart budget trips. Must be called from inside a scheduled fiber. If the budget is exhausted, shuts down all children and throws Acme::Parataxis::Error::Supervisor.
stop( )
$sup->stop;
Signals the supervisor to shut down. Safe to invoke from any fiber (including from within a child actor's handler). Stops all child instances, drains their teardown hooks, and causes run() to return cleanly.
child( $name )
my $actor = $sup->child('worker');
Returns the live instance currently running for the named child. Returns undef before run() has started. Croaks if $name is unregistered.
children( )
Returns the list of registered child names in start order.
restarts( [$name] )
my $worker_restarts = $sup->restarts('worker');
my $total_restarts = $sup->restarts;
Returns the restart count for a specific child, or the aggregated total across the entire tree if called without arguments.
running( )
Returns true if run() is actively executing.
strategy( ) / max_restarts( ) / within( )
Read-only configuration accessors.
Recipes
Building a multi-level supervision tree
Supervisors can supervise other supervisors. Inner supervisors heal local failures within their own budget; the outer supervisor only steps in if the entire inner subtree collapses:
async {
my $db_tree = Acme::Parataxis::Supervisor->new( max_restarts => 2, within => 10 );
$db_tree->supervise( sub { spawn_db_worker() }, name => 'primary' );
my root_tree = Acme::Parataxis::Supervisor->new( max_restarts => 5, within => 60 );
$root_tree->supervise( $db_tree, name => 'database_subsystem' );
$root_tree->supervise( sub { spawn_api_worker() }, name => 'api' );
fiber {
await_sleep(50);
$root_tree->stop;
};
$root_tree->run;
};
Behavior Notes
supervised => 1is required on actorsBy default, an actor swallows exceptions in its message handler and continues running. To enable supervision, actors must be spawned with
supervised => 1; this tells the actor to terminate when its handler dies, triggering the supervisor's restart mechanism.Restarts replace instances
A restarted actor is a completely new object with a fresh mailbox. Callers holding a reference to the old actor object will find it stopped. Retrieve the active instance via
$sup->child('name')or use registered named actors.Manual stops count as crashes
The supervisor detects child termination via death hooks without judging intent. If you call
$actor->stopon a supervised child directly, the supervisor interprets this as a termination and restarts it. To permanently retire a child, stop the enclosing supervisor.
SEE ALSO
Acme::Parataxis, Acme::Parataxis::Actor, Acme::Parataxis::Nursery, Acme::Parataxis::Error