saluki_core/runtime/
mod.rs

1//! Runtime system.
2//!
3//! This module contains the core components of the runtime system, including supervisors and processes. It's directly
4//! inspired by [Erlang/OTP](https://www.erlang.org/docs/28/system/design_principles#supervision-trees).
5//!
6//! To quote the Erlang/OTP documentation:
7//!
8//! > Workers are processes that perform computations and other actual work. Supervisors are processes that monitor
9//! > workers. A supervisor can restart a worker if something goes wrong. The supervision tree is a hierarchical
10//! > arrangement of code into supervisors and workers, which makes it possible to design and program fault-tolerant
11//! > software.
12//!
13//! # Processes
14//!
15//! An asynchronous system is composed of independent units of computation running concurrently, such as a set of tasks
16//! executing on a thread pool. We refer to these as **processes.** In other systems, these might be called _actors_,
17//! _tasks_, _fibers_, _virtual threads_, _goroutines_, or something else. Processes are lightweight and able to be
18//! (generally) created and destroyed cheaply.
19//!
20//! Processes have a few key attributes and invariants:
21//!
22//! - every process is a future that runs as an independent asynchronous task on a Tokio runtime
23//! - every process has a unique numerical identifier and a semi-unique name
24//!
25//! Unlike Erlang processes, Saluki processes don't have an inherent mailbox or message passing capabilities. As well,
26//! processes can't run by themselves. They must be _supervised_.
27//!
28//! # Supervisors
29//!
30//! Supervisors are themselves processes whose only job is to _supervise_ other processes, also called _workers_. In a
31//! supervisor, workers are added and configured through a common convention that allows defining how the worker is
32//! created (or recreated on failure), how many times it can be restarted, and more. Supervisors themselves can also be
33//! workers, and so nested _supervision trees_ can be constructed.
34//!
35//! Supervisors include a number of configurable settings that allow customizing the behavior of how workers are
36//! managed, which in turn allows building fault-tolerant systems: we can restart workers for transient failures, give
37//! up for permanent failures, and so on.
38//!
39//! # Spawning children
40//!
41//! Children are usually declared up front with [`Supervisor::add_worker`], but a supervisor can also take on new
42//! children while it runs. There are exactly two ways to do that, and they differ only in how the supervisor is
43//! identified:
44//!
45//! - [`spawn`], which targets the _ambient_ supervisor: the one supervising the currently running process. This is the
46//!   [`tokio::spawn`] of supervision, and is what code running under supervision should reach for.
47//! - [`SupervisorHandle`], which names a supervisor explicitly.
48//!
49//! Either way, a child is described with [`ChildBuilder`] -- reached through [`worker`] and [`supervisable`] for the
50//! ambient supervisor, or the identically named methods on [`SupervisorHandle`] for a named one. The builder is the
51//! only way to configure a child: [`ChildSpecification`] is the description it produces and carries no settings of its
52//! own, so a combination the builder declines to offer can't be assembled around it.
53//!
54//! A child that is itself a [`Supervisor`] can be handed to any of the above directly, which is all most callers need.
55//! [`nested_supervisor`] (and [`SupervisorHandle::nested_supervisor`]) exists for the two settings that aren't
56//! reachable that way -- the restart policy and significance. It matters most for a dynamically spawned subtree, which
57//! would otherwise be [`temporary`][RestartType::Temporary] and so quietly stay dead once it terminated.
58//!
59//! Both are synchronous and infallible. As with [`tokio::spawn`], a child being accepted doesn't mean it will run: a
60//! supervisor that shuts down before it reaches the child never starts it at all.
61//!
62//! # Supervision trees
63//!
64//! As supervisors can be nested, this allows building a tree of supervisors (hence _supervision trees_) where leaf
65//! supervisors manage workers specific to a certain area, and parent supervisors manage the leaf supervisors. For
66//! example, for a server application serving multiple API endpoints, each endpoint might be managed by a separate
67//! supervisor: a worker for accepting connections, a worker for each connection, and so on. Above those supervisors, a
68//! parent supervisor manages each leaf supervisor, and potentially other workers that provide necessary services
69//! utilized by each endpoint, such as logging, metrics, or other infrastructure services.
70//!
71//! As every supervisor can define its own specific restart strategy, and behavior, this allows for more granular
72//! grouping and control over which set of workers must be restarted if a related worker fails, and how those failures
73//! propagate up and down the supervision tree.
74//!
75//! # Inspecting the tree
76//!
77//! A supervision tree is also a description of what a process is doing, which makes it worth being able to read at
78//! runtime. [`Supervisor::tree_handle`] returns a read-only handle whose [`snapshot`][SupervisionTreeHandle::snapshot]
79//! walks the tree from that supervisor downward, reporting each node's name, process, restart policy, restart count,
80//! lifetime, and resource usage, with its children nested beneath it. A handle can be taken before the supervisor
81//! starts and stays valid across every restart of it, which makes it suitable for handing to a worker that serves an
82//! operator-facing diagnostics endpoint.
83//!
84//! # Examples
85//!
86//! See the `basic_supervisor` example which shows how supervisors and workers are composed together, as well as how
87//! failed workers and supervisors are restarted.
88
89mod process;
90pub use self::process::get_sanitized_name;
91pub use self::process::Id as ProcessId;
92#[cfg(test)]
93pub(crate) use self::process::Name;
94
95pub mod state;
96
97mod dedicated;
98pub use self::dedicated::{RuntimeConfiguration, RuntimeMode};
99
100mod restart;
101pub use self::restart::{RestartMode, RestartStrategy, RestartType};
102
103mod supervisable;
104pub use self::supervisable::{InitializationError, ShutdownStrategy, Supervisable, SupervisorFuture};
105
106mod supervisor;
107pub use self::supervisor::{
108    AutoShutdown, ChildId, ChildSpecification, ChildState, LoweredChild, Supervisor, SupervisorError, SupervisorHandle,
109    SupervisorSpec, WorkerSpec,
110};
111
112mod tree;
113pub use self::tree::{
114    NodeKind, NodeSnapshot, NodeState, ResourceUsage, SupervisionSettings, SupervisionTreeAPIHandler,
115    SupervisionTreeHandle, SupervisionTreeState, SupervisionTreeWorker, TreeSnapshot, TreeTotals, UnixMillis,
116    SUPERVISION_TREE_ROUTE,
117};
118
119mod spawn;
120pub use self::spawn::spawn;
121
122mod builder;
123pub use self::builder::{
124    nested_supervisor, supervisable, worker, BuilderState, CanTerminate, ChildBuilder, NestedSupervisorBuilder,
125    OneShot, Restartable, Terminable,
126};
127
128mod workers;
129pub use self::workers::{FnWorker, IntoWorkerResult};
130
131mod worker_state;