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//! # Examples
76//!
77//! See the `basic_supervisor` example which shows how supervisors and workers are composed together, as well as how
78//! failed workers and supervisors are restarted.
79
80mod process;
81pub(crate) use self::process::get_sanitized_name;
82pub use self::process::Id as ProcessId;
83#[cfg(test)]
84pub(crate) use self::process::Name;
85
86pub mod state;
87
88mod dedicated;
89pub use self::dedicated::{RuntimeConfiguration, RuntimeMode};
90
91mod restart;
92pub use self::restart::{RestartMode, RestartStrategy, RestartType};
93
94mod supervisor;
95pub use self::supervisor::{
96 AutoShutdown, ChildId, ChildSpecification, ChildState, InitializationError, LoweredChild, ShutdownStrategy,
97 Supervisable, Supervisor, SupervisorError, SupervisorFuture, SupervisorHandle, SupervisorSpec, WorkerSpec,
98};
99
100mod spawn;
101pub use self::spawn::spawn;
102
103mod builder;
104pub use self::builder::{
105 nested_supervisor, supervisable, worker, BuilderState, CanTerminate, ChildBuilder, NestedSupervisorBuilder,
106 OneShot, Restartable, Terminable,
107};
108
109mod workers;
110pub use self::workers::{FnWorker, IntoWorkerResult};
111
112mod worker_state;