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;