mecha10_runtime/
runtime.rs1use 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
17pub 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 pub fn builder() -> RuntimeBuilder {
30 RuntimeBuilder::default()
31 }
32
33 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 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 tokio::time::timeout(
51 self.config.shutdown_timeout,
52 self.wait_shutdown()
53 ).await??;
54 }
55 }
56
57 Ok(())
58 }
59
60 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 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 tokio::time::timeout(
81 self.config.shutdown_timeout,
82 self.wait_shutdown()
83 ).await??;
84 }
85 }
86
87 Ok(())
88 }
89
90 async fn wait_shutdown(&self) -> Result<()> {
92 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 pub fn health_checker(&self) -> &Arc<HealthChecker> {
105 &self.health_checker
106 }
107
108 pub fn supervisor(&self) -> &Arc<Supervisor> {
110 &self.supervisor
111 }
112
113 pub fn launcher(&self) -> Option<&Arc<Launcher>> {
115 self.launcher.as_ref()
116 }
117
118 pub fn lifecycle(&self) -> Option<&LifecycleManager> {
120 self.lifecycle.as_ref()
121 }
122
123 pub fn shutdown(&self) -> &ShutdownHandle {
125 &self.shutdown
126 }
127}
128
129#[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 into, log_level, config.log_level, impl Into<String>
141 );
142
143 builder_setter!(
144 assign, log_format, config.log_format, LogFormat
146 );
147
148 builder_setter!(
149 assign, restart_policy, config.restart_policy, RestartPolicy
151 );
152
153 builder_setter!(
154 assign, health_check_interval, config.health_check_interval, Duration
156 );
157
158 builder_setter!(
159 assign, shutdown_timeout, config.shutdown_timeout, Duration
161 );
162
163 builder_setter!(
164 assign, with_launcher, enable_launcher, bool
166 );
167
168 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 pub fn build(self) -> Runtime {
181 init_logging(&self.config.log_level, self.config.log_format.clone());
183
184 let shutdown = ShutdownHandle::new();
186
187 let health_checker = Arc::new(HealthChecker::new(self.config.health_check_interval));
189
190 let supervisor = Arc::new(Supervisor::new(
192 health_checker.clone(),
193 shutdown.clone(),
194 self.config.restart_policy.clone(),
195 ));
196
197 let launcher = if self.enable_launcher {
199 Some(Arc::new(Launcher::new(supervisor.clone())))
200 } else {
201 None
202 };
203
204 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}