Logo ProcessKit API Reference

Supervisor Type

Keeps a `Command` alive: runs it, classifies every exit against the `RestartPolicy` and the `StopWhen` predicate, and restarts it after an exponential-backoff delay until supervision ends. `Command.Retry` answers "run this once, replaying on failure"; a supervisor answers the different question **"keep this alive"** — a minimal `runit`/`systemd`-style keeper on top of the runner layer. The two are distinct layers: a supervised command's own `Retry` is **not** applied per incarnation (supervision runs the bare runner), so use the supervisor's own restart policy and backoff instead. Runs go through an `IProcessRunner` (the default `JobRunner`); override with `WithRunner` to share a `ProcessGroup` or inject a test double. Defaults: `OnCrash`, unlimited restarts, backoff `200ms × 2.0` capped at 30 s, jitter on, failure-storm guard off (enable with `StormPause`; failure half-life 30 s, threshold 5.0). **Observability while supervision runs.** `RunAsync` only reports its `SupervisionOutcome` at the very end, which is unusable for a long-lived (potentially never-ending) supervised service — so two callback seams, `OnRestart` and `OnStormPause`, report restarts and storm pauses *live*, as they happen (e.g. for a health check or crash-loop alerting). Both callbacks are invoked synchronously, from the supervision loop itself (the same async context driving `RunAsync`), right before the corresponding delay is slept out — so a slow or blocking handler delays every restart/pause; keep handlers quick and non-blocking. Neither callback changes `SupervisionOutcome`'s semantics — `Restarts`/`StormPauses`/`Stopped` are unaffected and remain the authoritative final tally; the callbacks are an additive, best-effort live view.

Constructors

Constructor Description

Supervisor(command)

Full Usage: Supervisor(command)

Parameters:
Returns: Supervisor

Supervise `command` with the default `JobRunner` (a fresh private kill-on-drop group per incarnation).

command : Command
Returns: Supervisor

Instance members

Instance member Description

this.Backoff

Full Usage: this.Backoff

Parameters:
Returns: Supervisor

Exponential backoff before each restart: the delay is `base × factor^n`, capped by `MaxBackoff`, where `n` is an escalation exponent that climbs by one per restart but **resets to 0 after a healthy incarnation** (one that stayed up at least as long as `MaxBackoff` and wasn't a hang killed by its timeout) — so a long-lived service that crashes occasionally restarts promptly instead of being pinned at the ceiling. `n` is not the lifetime restart count (`SupervisionOutcome.Restarts`). A `factor` below `1.0` (or non-finite) is treated as `1.0`. Default: `200ms × 2.0`.

baseDelay : TimeSpan
factor : float
Returns: Supervisor

this.Capture

Full Usage: this.Capture

Parameters:
Returns: Supervisor

Bound (or widen) the output captured from each incarnation. The default is a bounded tail; pass `OutputBufferPolicy.Unbounded` to retain everything.

policy : OutputBufferPolicy
Returns: Supervisor

this.FailureDecay

Full Usage: this.FailureDecay

Parameters:
Returns: Supervisor

Half-life of the failure score used by the storm guard (default: 30 s). A zero half-life keeps no history. No effect unless `StormPause` is set.

decay : TimeSpan
Returns: Supervisor

this.FailureThreshold

Full Usage: this.FailureThreshold

Parameters:
    threshold : float

Returns: Supervisor

Failure score above which the storm guard trips (default: `5.0`). A non-finite threshold never trips. No effect unless `StormPause` is set.

threshold : float
Returns: Supervisor

this.GiveUpWhen

Full Usage: this.GiveUpWhen

Parameters:
Returns: Supervisor

Classify a crash — or a spawn/IO failure that never produced a result — as *permanent*, so the supervisor gives up instead of restarting it forever. `classifier` receives the `ProcessError` of the failed incarnation: for a crashed run (one that produced a `ProcessResult` but is not a success) that is the crash's own `ProcessResult.FailureError` projection; for a run that never produced a result at all, it is the runner's own error. This is a different seam than `StopWhen`, which classifies by *outcome* (`ProcessResult`) — `GiveUpWhen` classifies by *error kind*, independent of whether the incarnation ever ran. Not checked for a clean exit, nor for a run `StopWhen` already ended, nor for a crash the `RestartPolicy` itself would not have restarted (e.g. under `Never`) — those already stop supervision with a more specific reason. When checked, it runs *before* `MaxRestarts`: a permanent-failure verdict wins over "budget not yet exhausted". A crashed match reports `StopReason.GaveUp`; a match on a run that never produced a result has no result to report and surfaces the classified error directly as `RunAsync`'s `Error`, same as an exhausted budget on that path. Default: unset — a permanent failure restarts forever (throttled only by backoff/`MaxRestarts`/the storm guard), matching the prior behavior.

classifier : Func<ProcessError, bool>
Returns: Supervisor

this.Jitter

Full Usage: this.Jitter

Parameters:
    enabled : bool

Returns: Supervisor

Multiply each backoff delay by a uniform factor in `[0.5, 1.5)` (default: **on**), so a fleet of supervised workers restarted by the same incident does not stampede back in lockstep. Disable for deterministic delays.

enabled : bool
Returns: Supervisor

this.MaxBackoff

Full Usage: this.MaxBackoff

Parameters:
Returns: Supervisor

Cap any single backoff delay (default: 30 s).

cap : TimeSpan
Returns: Supervisor

this.MaxRestarts

Full Usage: this.MaxRestarts

Parameters:
    count : int

Returns: Supervisor

Restart at most `count` times — `count + 1` total runs (default: unlimited). `count` must be non-negative (`0` means no restarts at all — a single run; a negative value is rejected with `ArgumentOutOfRangeException`).

count : int
Returns: Supervisor

this.OnRestart

Full Usage: this.OnRestart

Parameters:
Returns: Supervisor

Observe restarts live: `handler` runs synchronously, from the supervision loop, right before each restart's backoff delay is slept out — after the failed/finished incarnation, before the next one starts. Invoked on every restart (a crash, a timeout, or a retried transient runner error), never for the initial run. `handler` runs on the same async context driving `RunAsync`, so keep it quick and non-blocking — a slow handler delays every restart. Purely additive: does not change `SupervisionOutcome.Restarts` or any other final semantics. Default: unset.

handler : Action<SupervisorRestartEvent>
Returns: Supervisor

this.OnStormPause

Full Usage: this.OnStormPause

Parameters:
Returns: Supervisor

Observe failure-storm pauses live: `handler` runs synchronously, from the supervision loop, right before each pause is slept out — see `StormPause`. Same synchronous, keep-it-quick contract as `OnRestart`. No effect unless `StormPause` is set. Purely additive: does not change `SupervisionOutcome.StormPauses` or any other final semantics. Default: unset.

handler : Action<SupervisorStormPauseEvent>
Returns: Supervisor

this.Restart

Full Usage: this.Restart

Parameters:
Returns: Supervisor

When to restart (default: `OnCrash`).

policy : RestartPolicy
Returns: Supervisor

this.RunAsync

Full Usage: this.RunAsync

Parameters:
Returns: Task<Result<SupervisionOutcome, ProcessError>>

Supervise until the policy, the predicate, or the restart budget ends it, and report the `SupervisionOutcome`. Returns `Error` only when the *terminating* attempt failed to produce a result at all (a spawn/IO failure with no further restart allowed) — there is no final result to report. A spawn failure with restarts remaining counts as a crash and is retried. An incarnation cancelled via its token is terminal: supervision returns that `Cancelled` immediately, regardless of policy or budget.

?cancellationToken : CancellationToken
Returns: Task<Result<SupervisionOutcome, ProcessError>>

this.StopWhen

Full Usage: this.StopWhen

Parameters:
Returns: Supervisor

End supervision when `predicate` matches a completed run — checked before the `RestartPolicy` on every exit, clean or not. (It never sees a run that failed to *start*; spawn errors are classified by the policy alone.)

predicate : Func<ProcessResult<string>, bool>
Returns: Supervisor

this.StormPause

Full Usage: this.StormPause

Parameters:
Returns: Supervisor

Enable the **failure-storm guard**: when crash-restarts cluster faster than the failure score can decay, pause restarts once for `pause` (jittered per `Jitter`), then reset the score and resume. Off by default. Pauses taken are reported in `SupervisionOutcome.StormPauses`.

pause : TimeSpan
Returns: Supervisor

this.WithRunner

Full Usage: this.WithRunner

Parameters:
Returns: Supervisor

Run every incarnation through `runner` instead of the default `JobRunner` — e.g. a shared `ProcessGroup` runner for one kill-on-drop group, or a test double.

runner : IProcessRunner
Returns: Supervisor

Type something to start searching.