mecha10_core/
service_manager.rs

1//! Service Manager for Lifecycle Management
2//!
3//! This module provides centralized management of Service lifecycles, including
4//! registration, startup, shutdown, and health monitoring.
5//!
6//! # Architecture
7//!
8//! ```text
9//! ┌────────────────────────────────────────────────────────────┐
10//! │                    ServiceManager                           │
11//! ├────────────────────────────────────────────────────────────┤
12//! │                                                            │
13//! │  1. Register services (init phase)                         │
14//! │     ├─ HTTP API Service                                    │
15//! │     ├─ Database Service                                    │
16//! │     └─ Job Processor Service                               │
17//! │                                                            │
18//! │  2. Start all services (in registration order)             │
19//! │     └─ Spawn background tasks                              │
20//! │                                                            │
21//! │  3. Monitor health                                         │
22//! │     └─ Periodic health checks                              │
23//! │                                                            │
24//! │  4. Stop all services (reverse order)                      │
25//! │     └─ Graceful shutdown                                   │
26//! │                                                            │
27//! └────────────────────────────────────────────────────────────┘
28//! ```
29//!
30//! # Example
31//!
32//! ```rust
33//! use mecha10::prelude::*;
34//! use mecha10::service::{Service, ServiceManager};
35//!
36//! # async fn example() -> Result<()> {
37//! // Create service manager
38//! let mut service_manager = ServiceManager::new();
39//!
40//! // Register services
41//! service_manager.register::<HttpApiService>(http_config).await?;
42//! service_manager.register::<DatabaseService>(db_config).await?;
43//!
44//! // Start all services
45//! service_manager.start_all().await?;
46//!
47//! // ... application runs ...
48//!
49//! // Wait for shutdown signal
50//! tokio::signal::ctrl_c().await?;
51//!
52//! // Stop all services (in reverse order)
53//! service_manager.stop_all().await?;
54//! # Ok(())
55//! # }
56//! # #[derive(Debug)] struct HttpApiService;
57//! # #[derive(Debug)] struct DatabaseService;
58//! # #[derive(Debug, serde::Deserialize)] struct HttpConfig;
59//! # #[derive(Debug, serde::Deserialize)] struct DbConfig;
60//! # #[async_trait::async_trait]
61//! # impl Service for HttpApiService {
62//! #     type Config = HttpConfig;
63//! #     async fn init(_: Self::Config) -> Result<Self> { Ok(Self) }
64//! #     async fn start(&mut self) -> Result<()> { Ok(()) }
65//! #     async fn stop(&mut self) -> Result<()> { Ok(()) }
66//! #     fn name(&self) -> &str { "http" }
67//! # }
68//! # #[async_trait::async_trait]
69//! # impl Service for DatabaseService {
70//! #     type Config = DbConfig;
71//! #     async fn init(_: Self::Config) -> Result<Self> { Ok(Self) }
72//! #     async fn start(&mut self) -> Result<()> { Ok(()) }
73//! #     async fn stop(&mut self) -> Result<()> { Ok(()) }
74//! #     fn name(&self) -> &str { "db" }
75//! # }
76//! # let http_config = HttpConfig;
77//! # let db_config = DbConfig;
78//! ```
79
80use crate::service::Service;
81use crate::{HealthStatus, Result};
82use async_trait::async_trait;
83use tokio::sync::broadcast;
84use tracing::{debug, error, info};
85
86// ============================================================================
87// Type-Erased Service Wrapper
88// ============================================================================
89
90/// Internal trait for type-erased service management.
91///
92/// This trait wraps the `Service` trait and erases the associated `Config` type,
93/// allowing services with different config types to be stored in a single collection.
94#[async_trait]
95trait ServiceWrapper: Send + Sync {
96    async fn start(&mut self) -> Result<()>;
97    async fn stop(&mut self) -> Result<()>;
98    async fn health_check(&self) -> HealthStatus;
99    fn name(&self) -> &str;
100}
101
102/// Wrapper that implements ServiceWrapper for any Service type.
103struct ServiceBox<S: Service> {
104    service: S,
105}
106
107#[async_trait]
108impl<S: Service> ServiceWrapper for ServiceBox<S> {
109    async fn start(&mut self) -> Result<()> {
110        self.service.start().await
111    }
112
113    async fn stop(&mut self) -> Result<()> {
114        self.service.stop().await
115    }
116
117    async fn health_check(&self) -> HealthStatus {
118        self.service.health_check().await
119    }
120
121    fn name(&self) -> &str {
122        self.service.name()
123    }
124}
125
126// ============================================================================
127// Service Manager
128// ============================================================================
129
130/// Centralized manager for service lifecycles.
131///
132/// The ServiceManager is responsible for:
133/// - Registering services with configuration
134/// - Starting services in order
135/// - Monitoring service health
136/// - Stopping services gracefully (in reverse order)
137/// - Broadcasting shutdown signals
138///
139/// # Lifecycle
140///
141/// 1. **Register Phase** - `register()` services with config (calls `Service::init()`)
142/// 2. **Start Phase** - `start_all()` starts all services (calls `Service::start()`)
143/// 3. **Running Phase** - Services execute, manager monitors health
144/// 4. **Stop Phase** - `stop_all()` stops services in reverse order (calls `Service::stop()`)
145///
146/// # Example
147///
148/// ```rust
149/// # use mecha10::prelude::*;
150/// # use mecha10::service::{Service, ServiceManager};
151/// # async fn example() -> Result<()> {
152/// let mut manager = ServiceManager::new();
153///
154/// // Register services (init phase)
155/// manager.register::<MyService>(config).await?;
156///
157/// // Start all services
158/// manager.start_all().await?;
159///
160/// // Check health
161/// let health = manager.health_check_all().await;
162/// for (name, status) in health {
163///     println!("{}: {:?}", name, status);
164/// }
165///
166/// // Graceful shutdown
167/// manager.stop_all().await?;
168/// # Ok(())
169/// # }
170/// # #[derive(Debug)] struct MyService;
171/// # #[derive(Debug, serde::Deserialize)] struct MyConfig;
172/// # #[async_trait::async_trait]
173/// # impl Service for MyService {
174/// #     type Config = MyConfig;
175/// #     async fn init(_: Self::Config) -> Result<Self> { Ok(Self) }
176/// #     async fn start(&mut self) -> Result<()> { Ok(()) }
177/// #     async fn stop(&mut self) -> Result<()> { Ok(()) }
178/// #     fn name(&self) -> &str { "my_service" }
179/// # }
180/// # let config = MyConfig;
181/// ```
182pub struct ServiceManager {
183    /// Registered services (in registration order)
184    services: Vec<Box<dyn ServiceWrapper>>,
185
186    /// Shutdown broadcast channel
187    shutdown_tx: broadcast::Sender<()>,
188}
189
190impl ServiceManager {
191    /// Create a new ServiceManager.
192    ///
193    /// # Example
194    ///
195    /// ```rust
196    /// # use mecha10::service::ServiceManager;
197    /// let manager = ServiceManager::new();
198    /// ```
199    pub fn new() -> Self {
200        let (shutdown_tx, _) = broadcast::channel(1);
201        Self {
202            services: Vec::new(),
203            shutdown_tx,
204        }
205    }
206
207    /// Register a service with configuration.
208    ///
209    /// Calls `Service::init()` to initialize the service, then stores it
210    /// for later startup. Services are started in the order they are registered.
211    ///
212    /// # Arguments
213    ///
214    /// * `config` - Service-specific configuration
215    ///
216    /// # Returns
217    ///
218    /// * `Ok(())` - Service registered successfully
219    /// * `Err(_)` - Service initialization failed
220    ///
221    /// # Example
222    ///
223    /// ```rust
224    /// # use mecha10::prelude::*;
225    /// # use mecha10::service::{Service, ServiceManager};
226    /// # async fn example() -> Result<()> {
227    /// let mut manager = ServiceManager::new();
228    ///
229    /// // Register HTTP API
230    /// manager.register::<HttpApiService>(HttpApiConfig {
231    ///     host: "0.0.0.0".to_string(),
232    ///     port: 8080,
233    /// }).await?;
234    ///
235    /// // Register database
236    /// manager.register::<DatabaseService>(DatabaseConfig {
237    ///     url: "postgresql://localhost/mecha10".to_string(),
238    ///     max_connections: 20,
239    /// }).await?;
240    /// # Ok(())
241    /// # }
242    /// # #[derive(Debug)] struct HttpApiService;
243    /// # #[derive(Debug)] struct DatabaseService;
244    /// # #[derive(Debug, serde::Deserialize)] struct HttpApiConfig { host: String, port: u16 }
245    /// # #[derive(Debug, serde::Deserialize)] struct DatabaseConfig { url: String, max_connections: u32 }
246    /// # #[async_trait::async_trait]
247    /// # impl Service for HttpApiService {
248    /// #     type Config = HttpApiConfig;
249    /// #     async fn init(_: Self::Config) -> Result<Self> { Ok(Self) }
250    /// #     async fn start(&mut self) -> Result<()> { Ok(()) }
251    /// #     async fn stop(&mut self) -> Result<()> { Ok(()) }
252    /// #     fn name(&self) -> &str { "http_api" }
253    /// # }
254    /// # #[async_trait::async_trait]
255    /// # impl Service for DatabaseService {
256    /// #     type Config = DatabaseConfig;
257    /// #     async fn init(_: Self::Config) -> Result<Self> { Ok(Self) }
258    /// #     async fn start(&mut self) -> Result<()> { Ok(()) }
259    /// #     async fn stop(&mut self) -> Result<()> { Ok(()) }
260    /// #     fn name(&self) -> &str { "database" }
261    /// # }
262    /// ```
263    pub async fn register<S>(&mut self, config: S::Config) -> Result<()>
264    where
265        S: Service + 'static,
266    {
267        debug!("Registering service: {}", std::any::type_name::<S>());
268
269        let service = S::init(config).await?;
270        let name = service.name().to_string();
271
272        // Wrap the service in ServiceBox for type erasure
273        let boxed = Box::new(ServiceBox { service });
274
275        // Store the type-erased service
276        self.services.push(boxed);
277
278        info!("Registered service: {}", name);
279        Ok(())
280    }
281
282    /// Start all registered services.
283    ///
284    /// Services are started in the order they were registered. If any service
285    /// fails to start, the error is returned and remaining services are not started.
286    ///
287    /// # Returns
288    ///
289    /// * `Ok(())` - All services started successfully
290    /// * `Err(_)` - A service failed to start
291    ///
292    /// # Example
293    ///
294    /// ```rust
295    /// # use mecha10::prelude::*;
296    /// # use mecha10::service::ServiceManager;
297    /// # async fn example() -> Result<()> {
298    /// let mut manager = ServiceManager::new();
299    /// // ... register services ...
300    ///
301    /// // Start all services
302    /// manager.start_all().await?;
303    ///
304    /// info!("All services started successfully");
305    /// # Ok(())
306    /// # }
307    /// ```
308    pub async fn start_all(&mut self) -> Result<()> {
309        info!("Starting {} services", self.services.len());
310
311        for service in &mut self.services {
312            let name = service.name().to_string();
313            debug!("Starting service: {}", name);
314
315            if let Err(e) = service.start().await {
316                error!("Failed to start service '{}': {}", name, e);
317                return Err(e);
318            }
319
320            info!("Started service: {}", name);
321        }
322
323        info!("All services started successfully");
324        Ok(())
325    }
326
327    /// Stop all services gracefully.
328    ///
329    /// Services are stopped in **reverse order** of registration. This ensures
330    /// that dependent services are stopped first (e.g., stop API server before database).
331    ///
332    /// Each service's `stop()` method is called, even if previous services failed
333    /// to stop. All errors are logged, and the first error encountered is returned.
334    ///
335    /// # Returns
336    ///
337    /// * `Ok(())` - All services stopped successfully
338    /// * `Err(_)` - At least one service failed to stop
339    ///
340    /// # Example
341    ///
342    /// ```rust
343    /// # use mecha10::prelude::*;
344    /// # use mecha10::service::ServiceManager;
345    /// # async fn example() -> Result<()> {
346    /// # let mut manager = ServiceManager::new();
347    /// // ... services running ...
348    ///
349    /// // Graceful shutdown
350    /// tokio::signal::ctrl_c().await?;
351    /// manager.stop_all().await?;
352    /// # Ok(())
353    /// # }
354    /// ```
355    pub async fn stop_all(&mut self) -> Result<()> {
356        info!("Stopping {} services (in reverse order)", self.services.len());
357
358        let mut first_error = None;
359
360        // Stop in reverse order
361        for service in self.services.iter_mut().rev() {
362            let name = service.name().to_string();
363            debug!("Stopping service: {}", name);
364
365            match service.stop().await {
366                Ok(()) => {
367                    info!("Stopped service: {}", name);
368                }
369                Err(e) => {
370                    error!("Failed to stop service '{}': {}", name, e);
371                    if first_error.is_none() {
372                        first_error = Some(e);
373                    }
374                }
375            }
376        }
377
378        if let Some(err) = first_error {
379            error!("Service manager stopped with errors");
380            Err(err)
381        } else {
382            info!("All services stopped successfully");
383            Ok(())
384        }
385    }
386
387    /// Check health of all services.
388    ///
389    /// Calls `health_check()` on each registered service and returns a vector
390    /// of (name, status) tuples.
391    ///
392    /// # Returns
393    ///
394    /// * `Vec<(String, HealthStatus)>` - Health status for each service
395    ///
396    /// # Example
397    ///
398    /// ```rust
399    /// # use mecha10::prelude::*;
400    /// # use mecha10::service::ServiceManager;
401    /// # async fn example() -> Result<()> {
402    /// # let manager = ServiceManager::new();
403    /// let health = manager.health_check_all().await;
404    ///
405    /// for (name, status) in health {
406    ///     if !status.healthy {
407    ///         warn!("Service '{}' is unhealthy: {:?}", name, status.message);
408    ///     } else {
409    ///         info!("Service '{}' is healthy", name);
410    ///     }
411    /// }
412    /// # Ok(())
413    /// # }
414    /// ```
415    pub async fn health_check_all(&self) -> Vec<(String, HealthStatus)> {
416        let mut results = Vec::new();
417
418        for service in &self.services {
419            let name = service.name().to_string();
420            let status = service.health_check().await;
421
422            debug!("Health check for '{}': healthy={}", name, status.healthy);
423            results.push((name, status));
424        }
425
426        results
427    }
428
429    /// Get a broadcast receiver for shutdown signals.
430    ///
431    /// Services can use this to listen for shutdown signals and stop gracefully.
432    ///
433    /// # Returns
434    ///
435    /// * `broadcast::Receiver<()>` - Shutdown signal receiver
436    ///
437    /// # Example
438    ///
439    /// ```rust
440    /// # use mecha10::service::ServiceManager;
441    /// # async fn example() {
442    /// let manager = ServiceManager::new();
443    /// let mut shutdown_rx = manager.shutdown_signal();
444    ///
445    /// tokio::spawn(async move {
446    ///     shutdown_rx.recv().await.ok();
447    ///     // Service cleanup...
448    /// });
449    /// # }
450    /// ```
451    pub fn shutdown_signal(&self) -> broadcast::Receiver<()> {
452        self.shutdown_tx.subscribe()
453    }
454
455    /// Trigger shutdown by broadcasting to all listeners.
456    ///
457    /// This sends a signal to all `shutdown_signal()` receivers. Services
458    /// should listen to this signal to stop gracefully.
459    ///
460    /// Note: This does NOT call `stop_all()`. You must call `stop_all()`
461    /// separately to actually stop services.
462    ///
463    /// # Example
464    ///
465    /// ```rust
466    /// # use mecha10::service::ServiceManager;
467    /// # async fn example() {
468    /// let mut manager = ServiceManager::new();
469    ///
470    /// // Trigger shutdown signal
471    /// manager.shutdown();
472    ///
473    /// // Then stop all services
474    /// manager.stop_all().await.ok();
475    /// # }
476    /// ```
477    pub fn shutdown(&self) {
478        info!("Broadcasting shutdown signal");
479        let _ = self.shutdown_tx.send(());
480    }
481
482    /// Get the number of registered services.
483    ///
484    /// # Returns
485    ///
486    /// * `usize` - Number of services
487    ///
488    /// # Example
489    ///
490    /// ```rust
491    /// # use mecha10::service::ServiceManager;
492    /// # let manager = ServiceManager::new();
493    /// println!("Services registered: {}", manager.service_count());
494    /// ```
495    pub fn service_count(&self) -> usize {
496        self.services.len()
497    }
498
499    /// Get names of all registered services.
500    ///
501    /// # Returns
502    ///
503    /// * `Vec<String>` - Service names
504    ///
505    /// # Example
506    ///
507    /// ```rust
508    /// # use mecha10::service::ServiceManager;
509    /// # let manager = ServiceManager::new();
510    /// let names = manager.service_names();
511    /// println!("Services: {}", names.join(", "));
512    /// ```
513    pub fn service_names(&self) -> Vec<String> {
514        self.services.iter().map(|s| s.name().to_string()).collect()
515    }
516}
517
518impl Default for ServiceManager {
519    fn default() -> Self {
520        Self::new()
521    }
522}
523
524// ============================================================================
525// Tests
526// ============================================================================