Expand description
Runtime system.
This module contains the core components of the runtime system, including supervisors and processes. It’s directly inspired by Erlang/OTP.
To quote the Erlang/OTP documentation:
Workers are processes that perform computations and other actual work. Supervisors are processes that monitor workers. A supervisor can restart a worker if something goes wrong. The supervision tree is a hierarchical arrangement of code into supervisors and workers, which makes it possible to design and program fault-tolerant software.
§Processes
An asynchronous system is composed of independent units of computation running concurrently, such as a set of tasks executing on a thread pool. We refer to these as processes. In other systems, these might be called actors, tasks, fibers, virtual threads, goroutines, or something else. Processes are lightweight and able to be (generally) created and destroyed cheaply.
Processes have a few key attributes and invariants:
- every process is a future that runs as an independent asynchronous task on a Tokio runtime
- every process has a unique numerical identifier and a semi-unique name
Unlike Erlang processes, Saluki processes don’t have an inherent mailbox or message passing capabilities. As well, processes can’t run by themselves. They must be supervised.
§Supervisors
Supervisors are themselves processes whose only job is to supervise other processes, also called workers. In a supervisor, workers are added and configured through a common convention that allows defining how the worker is created (or recreated on failure), how many times it can be restarted, and more. Supervisors themselves can also be workers, and so nested supervision trees can be constructed.
Supervisors include a number of configurable settings that allow customizing the behavior of how workers are managed, which in turn allows building fault-tolerant systems: we can restart workers for transient failures, give up for permanent failures, and so on.
§Spawning children
Usually, you declare children in advance with Supervisor::add_worker. A process can also start new children
while it runs. There are three ways to do this. They differ in the owner of the new child:
spawn, which makes the new child a child of the current process. It is the equivalent oftokio::spawnfor supervision. It is the recommended way to spawn for code that runs under supervision.spawn_sibling, which gives the new child to the nearest supervisor above the current process. The child is a sibling of the current process only if that supervisor runs the process directly, and not in a scope.SupervisorHandle, which names a supervisor explicitly.
For all three ways, a ChildBuilder describes the child. To get a builder, use worker or supervisable.
For a named supervisor, use the methods with the same names on SupervisorHandle. The builder is the only way to
configure a child. It produces a ChildSpecification, which has no settings of its own. Because of this, you
cannot use the specification to make a combination of settings that the builder does not offer.
A child that is itself a Supervisor can be handed to any of the above directly, which is all most callers need.
nested_supervisor and SupervisorHandle::nested_supervisor give access to the settings that are not available
that way: whether the subtree is needed or hosted, and whether it stops as soon as its owner is told to stop.
All three ways to spawn are synchronous and cannot fail. As with tokio::spawn, an accepted child does not always
run. If its owner stops before it reaches the child, the owner never starts the child.
§Restart policy
You do not select the restart policy of a child directly. Two properties of the child decide it:
- Whether it can start again. A child reports this through
Supervisable::can_restart. ASupervisablebuilds its work each time it starts, so it can usually start again. A child that consumes something when it starts is one-shot. Examples are a child built from a closure, which consumes its body, and aSupervisablethat takes a connection. - Whether its owner can work without it. The owner of a needed child cannot work without it. The owner of a
hosted child only runs it. See
ChildBuilder::neededandChildBuilder::hosted.
Together, these two properties decide what occurs when a child stops without a stop request:
| Needed | Hosted | |
|---|---|---|
| Can start again | always restarted (Permanent) | restarted if it failed (Transient) |
| One-shot | its owner fails | removed, and its owner continues |
A child that stops because of a stop request is never restarted. A child of a scope that stops after the owner of the scope is told to stop counts as stopped by request. If a child stops more often than the restart limit of its owner allows, the owner fails. This applies to needed and hosted children. The next level of the supervision tree then handles the failure.
A child that can start again, and that you declare in advance with Supervisor::add_worker, is needed by default.
This is because it is part of the purpose of its supervisor. Every other child is hosted by default.
§Scopes
A supervisor owns its children. Every supervised worker also owns the children that it spawns, and keeps them in its scope.
Each of these children runs as a separate task. The worker does not finish until all of these children finish. When the body of the worker returns, the scope stops them, each according to its own shutdown strategy, as a supervisor does.
A value can also own a scope, so that the background tasks of a primitive stop when the primitive stops. See
scope::nested. You can also create a scope before the process that will own it exists, with UnadoptedScope.
This lets work spawned during the construction of something belong to the process that later runs that thing.
§Where to spawn a child
Spawn most children into the scope of the current process with spawn. Use a different way only when one of these
conditions is true:
- A value, not a process, must own the child. Spawn the child into a
scope::nestedguard. - A process other than the one that spawns the child must own it. Get the scope of the owner with
scope::current, and spawn the child withChildBuilder::spawn_in. - The child must continue to run after restarts of the process that spawns it. Make both of them children of a
Supervisor, and spawn the child through itsSupervisorHandle. - Children need their own restart strategy, resource group, or runtime. A scope restarts its children at most once
in each five-second period, for all of its children together. Put these children under a
Supervisor.
If a worker body only waits for a stop request while its children do the work, use a Supervisor instead.
§Supervision trees
As supervisors can be nested, this allows building a tree of supervisors (hence supervision trees) where leaf supervisors manage workers specific to a certain area, and parent supervisors manage the leaf supervisors. For example, for a server application serving multiple API endpoints, each endpoint might be managed by a separate supervisor: a worker for accepting connections, a worker for each connection, and so on. Above those supervisors, a parent supervisor manages each leaf supervisor, and potentially other workers that provide necessary services utilized by each endpoint, such as logging, metrics, or other infrastructure services.
As every supervisor can define its own specific restart strategy, and behavior, this allows for more granular grouping and control over which set of workers must be restarted if a related worker fails, and how those failures propagate up and down the supervision tree.
§Inspecting the tree
A supervision tree is also a description of what a process is doing, which makes it worth being able to read at
runtime. Supervisor::tree_handle returns a read-only handle whose snapshot
walks the tree from that supervisor downward, reporting each node’s name, process, restart policy, restart count,
lifetime, and resource usage, with its children nested beneath it. A handle can be taken before the supervisor
starts and stays valid across every restart of it, which makes it suitable for handing to a worker that serves an
operator-facing diagnostics endpoint.
§Examples
See the basic_supervisor example which shows how supervisors and workers are composed together, as well as how
failed workers and supervisors are restarted.
Re-exports§
pub use self::scope::Scope;pub use self::scope::ScopeError;pub use self::scope::ScopeGuard;pub use self::scope::UnadoptedScope;
Modules§
Structs§
- Child
Builder - Builder for a yet-to-be-started child task.
- ChildId
- Identifier for a child managed by a
Supervisor. - Child
Specification - A specification for a process to be added to a
Supervisor. - FnWorker
- A
Supervisableworker built from a plain future. - Lowered
Child - A child specification lowered into the supervisor’s internal representation.
- Nested
Supervisor Builder - Builder for a yet-to-be-started nested supervisor.
- Node
Snapshot - One node – a supervisor or a worker – in a
TreeSnapshot. - OneShot
- Marks a builder whose worker can only be started once.
- Process
Id - Process identifier.
- Resource
Usage - Cumulative resource usage for one resource group.
- Restart
Strategy - Restart strategy for a supervisor.
- Restartable
- Marks a builder whose worker can usually be started more than once.
- Runtime
Configuration - Configuration for a dedicated Tokio runtime.
- Supervision
Settings - How a supervisor supervises its children, and how it has fared.
- Supervision
TreeAPI Handler - An API handler for reporting the state of a supervision tree.
- Supervision
Tree Handle - A read-only handle for taking snapshots of a supervision tree.
- Supervision
Tree State - State used for the supervision tree API handler.
- Supervision
Tree Worker - A worker that exposes a supervision tree over the control plane.
- Supervisor
- Supervises a set of workers.
- Supervisor
Handle - A handle for spawning dynamic children on a running
Supervisor. - Supervisor
Spec - Child specification state for a supervisor.
- Tree
Snapshot - A point-in-time view of a supervision tree.
- Tree
Totals - Aggregate counts across a whole
TreeSnapshot. - Unix
Millis - A point in time, as milliseconds since the Unix epoch.
- Worker
Spec - Child specification state for a worker.
Enums§
- Auto
Shutdown - Policy for automatically shutting a supervisor down based on the termination of its significant children.
- Initialization
Error - Initialization errors.
- Node
Kind - Whether a node supervises other nodes or performs work itself.
- Node
State - Where a node is in its lifecycle.
- Restart
Mode - Restart mode for child processes.
- Restart
Type - Restart policy for an individual child process.
- Runtime
Mode - Controls which runtime a supervisor runs on.
- Shutdown
Strategy - Strategy for shutting down a process.
- Supervisor
Error - Supervisor errors.
Constants§
- SUPERVISION_
TREE_ ROUTE - API route serving a snapshot of a supervision tree.
Traits§
- Builder
State - The kind of worker a
ChildBuilderis describing. - Child
State - Child specification state.
- Into
Worker Result - An output type that a function-based worker can produce.
- Supervisable
- A supervisable process.
Functions§
- get_
sanitized_ name - Sanitizes
nameinto a single process-name segment. - nested_
supervisor - Creates a builder for a nested supervisor spawned into the current process.
- spawn
- Spawns a child of the current process.
- spawn_
sibling - Spawns a child onto the ambient supervisor, the nearest supervisor above the current process.
- supervisable
- Creates a builder for a supervisable child task of the current process.
- worker
- Creates a builder for a child task of the current process.
- worker_
with_ shutdown - Creates a builder for a child task of the current process, with a body that receives the shutdown signal.
Type Aliases§
- Supervisor
Future - A
Futurethat represents the execution of a supervised process.