Skip to main content

Supervision

Every actor in Nexus has a supervisor -- its parent. When a child actor throws an exception, the parent's supervision strategy decides what happens next: restart the failed child, stop it, resume it as if nothing happened, or escalate the failure further up the hierarchy.

SupervisionStrategy

SupervisionStrategy is a final readonly class that encapsulates the failure handling policy. Instances are created through named constructors -- the class constructor is private.

One-for-one

Only the failed child is acted upon. Siblings continue running undisturbed.

use Monadial\Nexus\Core\Supervision\SupervisionStrategy;
use Monadial\Nexus\Core\Duration;

// Default: 3 retries within 60 seconds, always restart
$strategy = SupervisionStrategy::oneForOne();

// Custom limits
$strategy = SupervisionStrategy::oneForOne(
maxRetries: 5,
window: Duration::seconds(120),
);

Signature:

public static function oneForOne(
int $maxRetries = 3,
?Duration $window = null,
?Closure $decider = null,
): SupervisionStrategy

When $window is null, it defaults to Duration::seconds(60). When $decider is null, the default decider always returns Directive::Restart.

All-for-one

When one child fails, all siblings are acted upon. Use this when children depend on each other and cannot function correctly if one of them is in a degraded state.

$strategy = SupervisionStrategy::allForOne(
maxRetries: 3,
window: Duration::seconds(60),
);

Signature:

public static function allForOne(
int $maxRetries = 3,
?Duration $window = null,
?Closure $decider = null,
): SupervisionStrategy

Exponential backoff

Restarts the failed child with increasing delays between attempts. Useful for transient failures like network timeouts or rate limits where immediate retries would make the problem worse.

$strategy = SupervisionStrategy::exponentialBackoff(
initialBackoff: Duration::millis(100),
maxBackoff: Duration::seconds(30),
maxRetries: 5,
multiplier: 2.0,
);

Signature:

public static function exponentialBackoff(
Duration $initialBackoff,
Duration $maxBackoff,
int $maxRetries = 3,
float $multiplier = 2.0,
?Closure $decider = null,
): SupervisionStrategy

The delay between the nth and *(n+1)*th restart is min(initialBackoff * multiplier^n, maxBackoff).

Directive

The Directive enum defines the four possible outcomes when a child fails:

DirectiveEffect
Directive::RestartStop the child and start a fresh instance with the same props. The mailbox is preserved.
Directive::StopPermanently stop the child. No restart.
Directive::ResumeIgnore the failure and continue processing the next message with the current state.
Directive::EscalateThe supervisor cannot handle this failure. Pass it to the supervisor's own parent.
use Monadial\Nexus\Core\Supervision\Directive;

Custom deciders

The $decider parameter accepts a closure that inspects the thrown exception and returns a Directive. This lets you vary the response based on exception type.

use Monadial\Nexus\Core\Supervision\Directive;
use Monadial\Nexus\Core\Supervision\SupervisionStrategy;

$strategy = SupervisionStrategy::oneForOne(
maxRetries: 5,
decider: fn(Throwable $e) => match (true) {
$e instanceof TransientError => Directive::Restart,
$e instanceof FatalError => Directive::Stop,
$e instanceof Overloaded => Directive::Resume,
default => Directive::Escalate,
},
);

When no decider is provided, the default always returns Directive::Restart:

static fn(Throwable $_): Directive => Directive::Restart

You can call decide() on a strategy to invoke the decider directly:

$directive = $strategy->decide($exception);

Applying a strategy

Attach a supervision strategy to an actor through Props::withSupervision():

use Monadial\Nexus\Core\Actor\Behavior;
use Monadial\Nexus\Core\Actor\Props;
use Monadial\Nexus\Core\Supervision\SupervisionStrategy;
use Monadial\Nexus\Core\Duration;

$behavior = Behavior::receive(
fn(ActorContext $ctx, object $msg): Behavior => Behavior::same(),
);

$props = Props::fromBehavior($behavior)->withSupervision(
SupervisionStrategy::exponentialBackoff(
initialBackoff: Duration::millis(200),
maxBackoff: Duration::seconds(10),
),
);

$ref = $system->spawn($props, 'my-actor');

The strategy governs how this actor supervises its children. If the actor itself fails, its parent's strategy applies.

Strategy properties

Every SupervisionStrategy exposes its configuration as public readonly properties:

PropertyTypeDescription
typeStrategyTypeOneForOne, AllForOne, or ExponentialBackoff
maxRetriesintMaximum restart attempts before giving up
windowDurationTime window for counting retries (one-for-one and all-for-one)
deciderClosurefn(Throwable): Directive
initialBackoffDurationFirst delay (exponential backoff only)
maxBackoffDurationCeiling delay (exponential backoff only)
multiplierfloatGrowth factor (exponential backoff only)