Expand description
A supervisor loop for a daemon-pipe child (issue #295).
A PrivateChild is an engine the app started itself and talks to over the daemon pipe. The
rightkit-job-queue engine supervisor needs a sidecar stdio hello frame, which a daemon
service does not speak; this one watches such a child with the two rightkit-child-process
trackers instead:
- poll: every
SupervisorConfig::poll_intervalthe loop asks whether the child has exited (try_wait, no reaping surprises: the supervisor owns the child); - probe: a caller-supplied closure says whether the child answers (the usual one is
ping_probe: connect with the child’s own secret, verify the host, callping). Each result is fed to aHealthTracker; - relaunch: an exit, an unhealthy child or a startup that never became healthy is stopped
(stdin closed first, then the tree is killed after
SupervisorConfig::stop_grace) and theRestartTrackerdecides: relaunch after its backoff, wait for demand, or give up (SupervisorEvent::Exhausted).
Decisions that are easy to get wrong, and what was chosen:
- While a generation has never answered a probe, failures are expected (it is starting), so
only
HealthOutcome::StartupTimedOutacts;Unhealthyacts only after the first success. - The first successful probe calls
RestartTracker::ready, so a child that comes up and later crashes starts again from the base backoff (per the restart policy’s own rules). - A failed launch (the closure returned an error) is charged to the restart budget like a crash, so a binary that cannot start is not retried at poll speed forever.
SupervisorHandle::restart_nowon a running child counts as an exit of that generation. After the policy gave up (SupervisorState::Exhausted) it forgets the budget and starts again: an explicit user start.- The probe runs on the supervisor thread and may block as long as its own deadline; give it a
short one (
ping_probetakes the bound as an argument). - The child is created, probed and stopped on the supervisor thread only; the launch closure is the one thing that crosses to it.
Structs§
- Supervisor
Config - How a child is watched and restarted.
- Supervisor
Handle - Control of a running supervisor. Dropping it stops the supervisor and its child.
- Supervisor
Status - A snapshot of the supervisor, from
SupervisorHandle::status.
Enums§
- Lock
State - The app’s lock state, as input to a restart (the lock mirror itself stays the app’s: a
replacement core starts locked exactly when the app says the lock blocked its data).
Unknownis the state of an unlock (or lock) request that got no answer because the child died first: it starts the replacement locked, so an unanswered unlock can never restart a child unlocked. - Supervisor
Event - What happened, content-free.
launchis the 1-based count of children started. - Supervisor
State - What the supervisor is doing now.
Constants§
- DEFAULT_
POLL_ INTERVAL - Default
SupervisorConfig::poll_interval. - DEFAULT_
STOP_ GRACE - Default
SupervisorConfig::stop_grace.
Functions§
- ping_
probe - A probe for
supervise: connect to the child’s service with the child’s own secret, verify the host whenexpectis given, and callping. True only for a{"pong": true}answer.timeoutbounds one probe (connect, hello and the call). UseHostExpectationwhenever the secret is sent: a secret goes to whatever answered at the endpoint. - supervise
- Start supervising.
launchmakes a child (normallyPrivateChild::spawnof anOwnedCommand, with the per-run suite made fresh each time if the child needs one);probesays whether the running child answers (seeping_probe);on_eventhears what happens. The first child is started at once, on the supervisor thread. - supervise_
adopted - Supervise a child the caller already spawned (and usually already verified: it counts
as launch 1, already ready, so its first crash restarts from the base backoff). The
child is transferred untouched — its stdin stays open and it is not probed before the
first poll. Replacements come from
launch, with the lock flag resolved as insupervise_with_lock_state. - supervise_
with_ lock_ state supervisefor a child whose replacement must start from the app’s lock state: every launch receiveslocked, resolved at launch time from arestart_now_with_lockoverride first, thenlock_state, thenfalse.LockState::Unknownresolves to locked, so an unlock the dying child never answered cannot restart it unlocked. The lock mirror that produces the state stays the app’s input.
Type Aliases§
- Lock
State Fn - Supplies the current
LockStateat each launch. Runs on the supervisor thread. - Supervisor
Event Fn - Callback for
SupervisorEvents. Runs on the supervisor thread; keep it short.