Hive.Supervisor

hive · API reference

OTP-style supervision trees.

Supervision: OTP restart strategies + Cats Effect's await flag.

A supervisor owns a sub-switch; children run under it, so switch cancellation is structured shutdown for free. The restart loop consults each child's Outcome.t. Backoff is exponential and capped; it resets after a child has stayed up longer than the cap.

v1 notes: no restart-intensity limit (a permanently crashing child keeps restarting at the capped backoff rate) and no jitter — both deliberate omissions, revisit with measurements.

type strategy = 
  | One_for_one (* Restart only the failed child. *)
  | One_for_all (* Restart all children if one fails. *)
  | Rest_for_one (* Restart the failed child and those started after it. *)
type restart = 
  | Permanent (* Always restarted. *)
  | Transient (* Restarted only after a Failed outcome. *)
  | Temporary (* Never restarted. *)
type backoff = {
  initial : float; (* First restart delay, seconds. *)
  max_delay : float; (* Cap; also the uptime after which it resets. *)
  multiplier : float; (* Exponential factor. *)
}
val default_backoff : backoff

{ initial = 0.05; max_delay = 5.0; multiplier = 2.0 }

type child = {
  join : unit Outcome.t Eio.Promise.t;
  shutdown : unit -> unit;
}

What a supervisor needs from a running child: a way to observe its end and a way to ask it to stop. Worker, Server and Task all provide both.

type child_spec

Declarative description of a child to start and possibly restart.

val child_spec : 
  ?restart:restart ->
  ?backoff:backoff ->
  id:string ->
  (Eio.Switch.t -> child) ->
  child_spec

child_spec ~id start describes a child. start receives the supervisor's sub-switch and is re-run on every restart. restart defaults to Permanent.

ocaml]
let spec =
  Hive.Supervisor.child_spec ~id:"logger" (fun sw ->
    Hive.Supervisor.child_of_fn sw (fun () -> run_logger ()))
val child_of_fn : Eio.Switch.t -> (unit -> unit) -> child

Adapt a plain thunk (run as a Task) into a supervisable child.

type t

A running supervisor.

val start : 
  sw:Eio.Switch.t ->
  clock:_ Eio.Time.clock ->
  ?strategy:strategy ->
  ?await:bool ->
  child_spec list ->
  t

Starts the children in order, then supervises. strategy defaults to One_for_one. await (default false) controls stop: true waits for running children to finish on their own; false asks each to shut down (Cats Effect default). Raises Invalid_argument on duplicate child ids.

ocaml]
let supervisor =
  Hive.Supervisor.start ~sw ~clock
    ~strategy:Hive.Supervisor.One_for_one
    [spec]
val stop : t -> unit

Ask the supervisor to stop (see await above). Idempotent.

val join : t -> unit Outcome.t Eio.Promise.t

Resolved when the supervisor itself finishes.