Skip to main content

mecha10_runtime/
runtime.rs

1//! Main runtime for Mecha10 nodes
2
3use crate::config::{LogFormat, RestartPolicy, RuntimeConfig};
4use crate::health::HealthChecker;
5use crate::launcher::Launcher;
6use crate::lifecycle::{LifecycleManager, ModeConfig};
7use crate::logging::init_logging;
8use crate::node::NodeRunner;
9use crate::shutdown::{wait_for_signal, ShutdownHandle};
10use crate::supervisor::Supervisor;
11use anyhow::Result;
12use mecha10_core::builder_setter;
13use std::collections::HashMap;
14use std::sync::Arc;
15use std::time::Duration;
16
17/// Main runtime for supervising and running nodes
18pub struct Runtime {
19    config: RuntimeConfig,
20    health_checker: Arc<HealthChecker>,
21    supervisor: Arc<Supervisor>,
22    launcher: Option<Arc<Launcher>>,
23    lifecycle: Option<LifecycleManager>,
24    shutdown: ShutdownHandle,
25}
26
27impl Runtime {
28    /// Create a new runtime builder
29    pub fn builder() -> RuntimeBuilder {
30        RuntimeBuilder::default()
31    }
32
33    /// Run a single node until completion or shutdown
34    pub async fn run_node(&mut self, name: &str, node: Box<dyn NodeRunner>) -> Result<()> {
35        tracing::info!("Starting runtime for node: {}", name);
36
37        let handle = self.supervisor.launch_node(node);
38
39        // Wait for either node completion or shutdown signal
40        tokio::select! {
41            result = handle.wait() => {
42                result?;
43                tracing::info!("Node {} completed", name);
44            }
45            _ = wait_for_signal() => {
46                tracing::info!("Shutdown signal received");
47                self.shutdown.shutdown();
48
49                // Wait for graceful shutdown with timeout
50                tokio::time::timeout(
51                    self.config.shutdown_timeout,
52                    self.wait_shutdown()
53                ).await??;
54            }
55        }
56
57        Ok(())
58    }
59
60    /// Run multiple nodes until all complete or shutdown
61    pub async fn run_nodes(&mut self, nodes: Vec<Box<dyn NodeRunner>>) -> Result<()> {
62        tracing::info!("Starting runtime for {} nodes", nodes.len());
63
64        let handles = self.supervisor.launch_nodes(nodes);
65
66        // Wait for either all nodes to complete or shutdown signal
67        tokio::select! {
68            _ = async {
69                for handle in handles {
70                    let _ = handle.wait().await;
71                }
72            } => {
73                tracing::info!("All nodes completed");
74            }
75            _ = wait_for_signal() => {
76                tracing::info!("Shutdown signal received");
77                self.shutdown.shutdown();
78
79                // Wait for graceful shutdown with timeout
80                tokio::time::timeout(
81                    self.config.shutdown_timeout,
82                    self.wait_shutdown()
83                ).await??;
84            }
85        }
86
87        Ok(())
88    }
89
90    /// Wait for all nodes to shut down
91    async fn wait_shutdown(&self) -> Result<()> {
92        // Health checker will track when nodes complete shutdown
93        loop {
94            let statuses = self.health_checker.check_all().await;
95            if statuses.is_empty() {
96                break;
97            }
98            tokio::time::sleep(Duration::from_millis(100)).await;
99        }
100        Ok(())
101    }
102
103    /// Get the health checker
104    pub fn health_checker(&self) -> &Arc<HealthChecker> {
105        &self.health_checker
106    }
107
108    /// Get the supervisor
109    pub fn supervisor(&self) -> &Arc<Supervisor> {
110        &self.supervisor
111    }
112
113    /// Get the launcher (if enabled)
114    pub fn launcher(&self) -> Option<&Arc<Launcher>> {
115        self.launcher.as_ref()
116    }
117
118    /// Get the lifecycle manager (if enabled)
119    pub fn lifecycle(&self) -> Option<&LifecycleManager> {
120        self.lifecycle.as_ref()
121    }
122
123    /// Get the shutdown handle
124    pub fn shutdown(&self) -> &ShutdownHandle {
125        &self.shutdown
126    }
127}
128
129/// Builder for creating a runtime with custom configuration
130#[derive(Default)]
131pub struct RuntimeBuilder {
132    config: RuntimeConfig,
133    enable_launcher: bool,
134    lifecycle_config: Option<(HashMap<String, ModeConfig>, String)>,
135}
136
137impl RuntimeBuilder {
138    builder_setter!(
139        /// Set log level (trace, debug, info, warn, error)
140        into, log_level, config.log_level, impl Into<String>
141    );
142
143    builder_setter!(
144        /// Set log format
145        assign, log_format, config.log_format, LogFormat
146    );
147
148    builder_setter!(
149        /// Set restart policy
150        assign, restart_policy, config.restart_policy, RestartPolicy
151    );
152
153    builder_setter!(
154        /// Set health check interval
155        assign, health_check_interval, config.health_check_interval, Duration
156    );
157
158    builder_setter!(
159        /// Set graceful shutdown timeout
160        assign, shutdown_timeout, config.shutdown_timeout, Duration
161    );
162
163    builder_setter!(
164        /// Enable launcher service for dynamic node management
165        assign, with_launcher, enable_launcher, bool
166    );
167
168    /// Enable lifecycle management with mode configuration
169    ///
170    /// # Arguments
171    ///
172    /// * `modes` - Map of mode name to mode configuration
173    /// * `default_mode` - Initial mode to start in
174    pub fn with_lifecycle(mut self, modes: HashMap<String, ModeConfig>, default_mode: String) -> Self {
175        self.lifecycle_config = Some((modes, default_mode));
176        self
177    }
178
179    /// Build the runtime
180    pub fn build(self) -> Runtime {
181        // Initialize logging
182        init_logging(&self.config.log_level, self.config.log_format.clone());
183
184        // Create shutdown handle
185        let shutdown = ShutdownHandle::new();
186
187        // Create health checker
188        let health_checker = Arc::new(HealthChecker::new(self.config.health_check_interval));
189
190        // Create supervisor
191        let supervisor = Arc::new(Supervisor::new(
192            health_checker.clone(),
193            shutdown.clone(),
194            self.config.restart_policy.clone(),
195        ));
196
197        // Create launcher if enabled
198        let launcher = if self.enable_launcher {
199            Some(Arc::new(Launcher::new(supervisor.clone())))
200        } else {
201            None
202        };
203
204        // Create lifecycle manager if enabled
205        let lifecycle = if let Some((modes, default_mode)) = self.lifecycle_config {
206            Some(LifecycleManager::new(
207                supervisor.clone() as Arc<dyn crate::lifecycle::SupervisorTrait>,
208                modes,
209                default_mode,
210            ))
211        } else {
212            None
213        };
214
215        Runtime {
216            config: self.config,
217            health_checker,
218            supervisor,
219            launcher,
220            lifecycle,
221            shutdown,
222        }
223    }
224}