task_supervisor/lib.rs
1//! # Task Supervisor for Tokio
2//!
3//! The `task-supervisor` crate provides a framework for managing and monitoring asynchronous tasks
4//! in Rust using the Tokio runtime. It ensures tasks remain operational by monitoring their health
5//! and automatically restarting them if they fail or become unresponsive. The supervisor also supports dynamic
6//! task management, allowing tasks to be added, restarted, or killed at runtime.
7//!
8//! ## Key Features
9//!
10//! - **Task Supervision**: Monitors tasks using heartbeats and restarts them if they crash or exceed a configurable timeout.
11//! - **Dynamic Task Management**: Add, restart, or kill tasks dynamically via the `SupervisorHandle`.
12//! - **Task Status Querying**: Retrieve the status of individual tasks or all tasks using the `SupervisorHandle`.
13//! - **Configurable Parameters**: Customize timeout thresholds, heartbeat intervals, and health check timings.
14//! - **Proper Task Cancellation**: Ensures tasks are properly stopped during restarts or when killed using a `CancellationToken`.
15//! - **Task Completion Handling**: Differentiates between tasks that complete successfully and those that fail, with appropriate actions.
16//!
17//! ## Key Components
18//!
19//! - **[`SupervisedTask`] Trait**: Defines the interface for tasks to be supervised. Tasks must implement the `run` method,
20//! which contains the task's logic and returns a `Result<TaskOutcome, Box<dyn std::error::Error + Send + Sync>>` indicating
21//! success or failure. Additionally, tasks must implement `clone_task` to allow the supervisor to create new instances for restarts.
22//!
23//! - **[`Supervisor`] Struct**: Manages a collection of tasks, monitors their health using heartbeats, and restarts them if necessary.
24//! It uses a message-passing system to handle internal events and user commands. The supervisor ensures that tasks are properly
25//! cancelled during restarts or when killed using a `CancellationToken`.
26//!
27//! - **[`SupervisorBuilder`]**: Provides a builder pattern to construct a `Supervisor` instance with configurable parameters such as
28//! timeout thresholds, heartbeat intervals, and health check timings. Allows adding initial tasks before starting the supervisor.
29//!
30//! - **[`SupervisorHandle`]**: Offers a handle to interact with a running supervisor, enabling dynamic operations like adding new tasks,
31//! restarting tasks, killing tasks, and shutting down the supervisor. It also provides methods to query the status of individual tasks
32//! or all tasks, and a `wait` method to await the completion of all tasks.
33//!
34//! - **[`TaskStatus`] Enum**: Represents the lifecycle states of a supervised task, such as `Created`, `Starting`, `Healthy`, `Failed`,
35//! `Completed`, or `Dead`.
36//!
37//! ## Usage Example
38//!
39//! Below is an example demonstrating how to define a supervised task, use the supervisor to manage it, and query task statuses:
40//!
41//! ```rust
42//! use async_trait::async_trait;
43//! use std::time::Duration;
44//! use task_supervisor::{SupervisedTask, SupervisorBuilder, TaskError, SupervisorHandleError};
45//!
46//! // A task need to be Clonable for now - so we can restart it easily.
47//! #[derive(Clone)]
48//! struct MyTask {
49//! pub emoji: char,
50//! }
51//!
52//! #[async_trait]
53//! impl SupervisedTask for MyTask {
54//! async fn run(&mut self) -> Result<(), TaskError> {
55//! for _ in 0..15 {
56//! println!("{} Task is running!", self.emoji);
57//! tokio::time::sleep(Duration::from_secs(1)).await;
58//! }
59//! println!("{} Task completed!", self.emoji);
60//! Ok(())
61//! }
62//! }
63//!
64//! #[tokio::main]
65//! async fn main() -> Result<(), SupervisorHandleError> {
66//! // Build the supervisor with initial tasks
67//! let supervisor = SupervisorBuilder::default().build();
68//!
69//! // Run the supervisor and get the handle
70//! let handle = supervisor.run();
71//!
72//! let h = handle.clone();
73//! tokio::spawn(async move {
74//! // Add a new task after 5 seconds
75//! tokio::time::sleep(Duration::from_secs(5)).await;
76//! println!("Adding a task after 5 seconds...");
77//! h.add_task("task", MyTask { emoji: '🆕' }).unwrap();
78//!
79//! // Query the task status after 2 seconds
80//! tokio::time::sleep(Duration::from_secs(2)).await;
81//! match h.get_task_status("task").await {
82//! Ok(Some(status)) => println!("Task status: {:?}", status),
83//! Ok(None) => println!("Task not found"),
84//! Err(e) => println!("Error getting task status: {}", e),
85//! }
86//!
87//! // Restart the task after 5 seconds
88//! tokio::time::sleep(Duration::from_secs(5)).await;
89//! println!("Restarting task after 5 seconds...");
90//! h.restart("task").unwrap();
91//!
92//! // Query all task statuses after 2 seconds
93//! tokio::time::sleep(Duration::from_secs(2)).await;
94//! match h.get_all_task_statuses().await {
95//! Ok(statuses) => {
96//! println!("All task statuses:");
97//! for (name, status) in statuses {
98//! println!(" {}: {:?}", name, status);
99//! }
100//! }
101//! Err(e) => println!("Error getting all task statuses: {}", e),
102//! }
103//!
104//! // Kill the task after another 5 seconds
105//! tokio::time::sleep(Duration::from_secs(5)).await;
106//! println!("Killing task after 5 seconds...");
107//! h.kill_task("task").unwrap();
108//!
109//! // Shutdown the supervisor
110//! h.shutdown().unwrap();
111//! });
112//!
113//! // Wait for all tasks to die
114//! handle.wait().await.unwrap();
115//! println!("All tasks died! 🫡");
116//! Ok(())
117//! }
118//! ```
119//!
120pub use supervisor::{
121 builder::SupervisorBuilder,
122 handle::{SupervisorHandle, SupervisorHandleError},
123 Supervisor, SupervisorError,
124};
125pub use task::{SupervisedTask, TaskError, TaskResult, TaskStatus};
126
127mod supervisor;
128mod task;
129
130pub type TaskName = String;