Skip to main content

sklears_compose/modular_framework/
lifecycle_management.rs

1//! Component Lifecycle Management System
2//!
3//! This module provides comprehensive lifecycle management capabilities for modular
4//! components including state tracking, dependency-aware initialization, graceful
5//! shutdown, and lifecycle event coordination.
6
7use serde::{Deserialize, Serialize};
8use sklears_core::error::{Result as SklResult, SklearsError};
9use std::collections::{HashMap, HashSet};
10use std::time::{Duration, SystemTime};
11
12/// Lifecycle manager for component state coordination
13///
14/// Manages component lifecycle states, dependency resolution, initialization
15/// ordering, and graceful shutdown procedures across the modular system.
16#[derive(Debug)]
17pub struct LifecycleManager {
18    /// Component states tracking
19    component_states: HashMap<String, ComponentLifecycleState>,
20    /// Component dependencies
21    dependencies: HashMap<String, Vec<String>>,
22    /// Lifecycle listeners
23    listeners: HashMap<LifecycleEvent, Vec<String>>,
24    /// Initialization order cache
25    initialization_order: Vec<String>,
26    /// Lifecycle configuration
27    config: LifecycleConfig,
28    /// Lifecycle metrics
29    metrics: LifecycleMetrics,
30}
31
32impl LifecycleManager {
33    /// Create a new lifecycle manager
34    #[must_use]
35    pub fn new() -> Self {
36        Self {
37            component_states: HashMap::new(),
38            dependencies: HashMap::new(),
39            listeners: HashMap::new(),
40            initialization_order: Vec::new(),
41            config: LifecycleConfig::default(),
42            metrics: LifecycleMetrics::new(),
43        }
44    }
45
46    /// Register a component with the lifecycle manager
47    pub fn register_component(
48        &mut self,
49        component_id: &str,
50        dependencies: Vec<String>,
51    ) -> SklResult<()> {
52        if self.component_states.contains_key(component_id) {
53            return Err(SklearsError::InvalidInput(format!(
54                "Component {component_id} already registered"
55            )));
56        }
57
58        self.component_states
59            .insert(component_id.to_string(), ComponentLifecycleState::Created);
60        self.dependencies
61            .insert(component_id.to_string(), dependencies);
62        self.metrics.total_components += 1;
63
64        // Invalidate initialization order cache
65        self.initialization_order.clear();
66
67        Ok(())
68    }
69
70    /// Unregister a component
71    pub fn unregister_component(&mut self, component_id: &str) -> SklResult<()> {
72        if let Some(state) = self.component_states.get(component_id) {
73            if matches!(state, ComponentLifecycleState::Running) {
74                return Err(SklearsError::InvalidInput(format!(
75                    "Cannot unregister running component {component_id}"
76                )));
77            }
78        }
79
80        self.component_states.remove(component_id);
81        self.dependencies.remove(component_id);
82        self.initialization_order.retain(|id| id != component_id);
83        self.metrics.total_components = self.metrics.total_components.saturating_sub(1);
84
85        Ok(())
86    }
87
88    /// Set component state
89    pub fn set_component_state(
90        &mut self,
91        component_id: &str,
92        state: ComponentLifecycleState,
93    ) -> SklResult<()> {
94        if !self.component_states.contains_key(component_id) {
95            return Err(SklearsError::InvalidInput(format!(
96                "Component {component_id} not registered"
97            )));
98        }
99
100        let old_state = self
101            .component_states
102            .get(component_id)
103            .cloned()
104            .unwrap_or(ComponentLifecycleState::Created);
105
106        // Validate state transition
107        if !self.is_valid_transition(&old_state, &state) {
108            return Err(SklearsError::InvalidInput(format!(
109                "Invalid state transition from {old_state:?} to {state:?}"
110            )));
111        }
112
113        self.component_states
114            .insert(component_id.to_string(), state.clone());
115
116        // Update metrics
117        match &state {
118            ComponentLifecycleState::Ready => self.metrics.ready_components += 1,
119            ComponentLifecycleState::Running => {
120                self.metrics.running_components += 1;
121                self.metrics.ready_components = self.metrics.ready_components.saturating_sub(1);
122            }
123            ComponentLifecycleState::Stopped => {
124                self.metrics.running_components = self.metrics.running_components.saturating_sub(1);
125            }
126            ComponentLifecycleState::Error(_) => self.metrics.failed_components += 1,
127            _ => {}
128        }
129
130        // Notify listeners
131        self.notify_lifecycle_event(LifecycleEvent::StateChanged {
132            component_id: component_id.to_string(),
133            old_state,
134            new_state: state,
135        })?;
136
137        Ok(())
138    }
139
140    /// Get component state
141    #[must_use]
142    pub fn get_component_state(&self, component_id: &str) -> Option<&ComponentLifecycleState> {
143        self.component_states.get(component_id)
144    }
145
146    /// Initialize all components in dependency order
147    pub fn initialize_all_components(&mut self) -> SklResult<InitializationResult> {
148        let start_time = SystemTime::now();
149        let mut result = InitializationResult {
150            total_components: self.component_states.len(),
151            successful_initializations: 0,
152            failed_initializations: 0,
153            initialization_order: Vec::new(),
154            duration: Duration::from_secs(0),
155            errors: Vec::new(),
156        };
157
158        // Calculate initialization order if not cached
159        if self.initialization_order.is_empty() {
160            self.initialization_order = self.calculate_initialization_order()?;
161        }
162
163        result.initialization_order = self.initialization_order.clone();
164
165        // Initialize components in order
166        let initialization_order = self.initialization_order.clone();
167        for component_id in &initialization_order {
168            match self.initialize_component(component_id) {
169                Ok(()) => {
170                    result.successful_initializations += 1;
171                    self.notify_lifecycle_event(LifecycleEvent::ComponentInitialized {
172                        component_id: component_id.clone(),
173                    })?;
174                }
175                Err(e) => {
176                    result.failed_initializations += 1;
177                    result
178                        .errors
179                        .push(format!("Failed to initialize {component_id}: {e}"));
180
181                    if self.config.fail_fast_initialization {
182                        result.duration = start_time.elapsed().unwrap_or(Duration::from_secs(0));
183                        return Ok(result);
184                    }
185                }
186            }
187        }
188
189        result.duration = start_time.elapsed().unwrap_or(Duration::from_secs(0));
190        self.metrics.last_initialization_time = result.duration;
191
192        Ok(result)
193    }
194
195    /// Initialize a specific component
196    pub fn initialize_component(&mut self, component_id: &str) -> SklResult<()> {
197        // Check if component exists
198        let current_state = self.component_states.get(component_id).ok_or_else(|| {
199            SklearsError::InvalidInput(format!("Component {component_id} not found"))
200        })?;
201
202        // Check if already initialized
203        if matches!(
204            current_state,
205            ComponentLifecycleState::Ready | ComponentLifecycleState::Running
206        ) {
207            return Ok(());
208        }
209
210        // Check dependencies are ready
211        if let Some(deps) = self.dependencies.get(component_id) {
212            for dep in deps {
213                let dep_state = self.component_states.get(dep).ok_or_else(|| {
214                    SklearsError::InvalidInput(format!("Dependency {dep} not found"))
215                })?;
216
217                if !matches!(
218                    dep_state,
219                    ComponentLifecycleState::Ready | ComponentLifecycleState::Running
220                ) {
221                    return Err(SklearsError::InvalidInput(format!(
222                        "Dependency {dep} not ready for {component_id}"
223                    )));
224                }
225            }
226        }
227
228        // Set initializing state
229        self.set_component_state(component_id, ComponentLifecycleState::Initializing)?;
230
231        // Simulate initialization (in real implementation, this would call component.initialize())
232        std::thread::sleep(Duration::from_millis(10)); // Simulate work
233
234        // Set ready state
235        self.set_component_state(component_id, ComponentLifecycleState::Ready)?;
236
237        Ok(())
238    }
239
240    /// Shutdown all components in reverse dependency order
241    pub fn shutdown_all_components(&mut self) -> SklResult<ShutdownResult> {
242        let start_time = SystemTime::now();
243        let mut result = ShutdownResult {
244            total_components: self.component_states.len(),
245            successful_shutdowns: 0,
246            failed_shutdowns: 0,
247            shutdown_order: Vec::new(),
248            duration: Duration::from_secs(0),
249            errors: Vec::new(),
250        };
251
252        // Reverse the initialization order for shutdown
253        let shutdown_order: Vec<String> = self.initialization_order.iter().rev().cloned().collect();
254        result.shutdown_order = shutdown_order.clone();
255
256        // Shutdown components in reverse order
257        for component_id in &shutdown_order {
258            match self.shutdown_component(component_id) {
259                Ok(()) => {
260                    result.successful_shutdowns += 1;
261                    self.notify_lifecycle_event(LifecycleEvent::ComponentShutdown {
262                        component_id: component_id.clone(),
263                    })?;
264                }
265                Err(e) => {
266                    result.failed_shutdowns += 1;
267                    result
268                        .errors
269                        .push(format!("Failed to shutdown {component_id}: {e}"));
270                }
271            }
272        }
273
274        result.duration = start_time.elapsed().unwrap_or(Duration::from_secs(0));
275        self.metrics.last_shutdown_time = result.duration;
276
277        Ok(result)
278    }
279
280    /// Shutdown a specific component
281    pub fn shutdown_component(&mut self, component_id: &str) -> SklResult<()> {
282        let current_state = self.component_states.get(component_id).ok_or_else(|| {
283            SklearsError::InvalidInput(format!("Component {component_id} not found"))
284        })?;
285
286        if matches!(current_state, ComponentLifecycleState::Stopped) {
287            return Ok(());
288        }
289
290        // Set stopping state
291        self.set_component_state(component_id, ComponentLifecycleState::Stopping)?;
292
293        // Simulate shutdown (in real implementation, this would call component.shutdown())
294        std::thread::sleep(Duration::from_millis(5)); // Simulate work
295
296        // Set stopped state
297        self.set_component_state(component_id, ComponentLifecycleState::Stopped)?;
298
299        Ok(())
300    }
301
302    /// Add a lifecycle event listener
303    pub fn add_listener(&mut self, event: LifecycleEvent, listener_id: &str) {
304        self.listeners
305            .entry(event)
306            .or_default()
307            .push(listener_id.to_string());
308    }
309
310    /// Remove a lifecycle event listener
311    pub fn remove_listener(&mut self, event: &LifecycleEvent, listener_id: &str) {
312        if let Some(listeners) = self.listeners.get_mut(event) {
313            listeners.retain(|id| id != listener_id);
314        }
315    }
316
317    /// Get lifecycle metrics
318    #[must_use]
319    pub fn get_metrics(&self) -> &LifecycleMetrics {
320        &self.metrics
321    }
322
323    /// Get all component states
324    #[must_use]
325    pub fn get_all_states(&self) -> &HashMap<String, ComponentLifecycleState> {
326        &self.component_states
327    }
328
329    /// Check if all components are in a specific state
330    #[must_use]
331    pub fn all_components_in_state(&self, state: &ComponentLifecycleState) -> bool {
332        self.component_states.values().all(|s| s == state)
333    }
334
335    /// Get components in a specific state
336    #[must_use]
337    pub fn get_components_in_state(&self, state: &ComponentLifecycleState) -> Vec<String> {
338        self.component_states
339            .iter()
340            .filter_map(|(id, s)| if s == state { Some(id.clone()) } else { None })
341            .collect()
342    }
343
344    fn calculate_initialization_order(&self) -> SklResult<Vec<String>> {
345        let mut order = Vec::new();
346        let mut visited = HashSet::new();
347        let mut visiting = HashSet::new();
348
349        for component_id in self.component_states.keys() {
350            if !visited.contains(component_id) {
351                self.topological_sort(component_id, &mut visited, &mut visiting, &mut order)?;
352            }
353        }
354
355        Ok(order)
356    }
357
358    fn topological_sort(
359        &self,
360        component_id: &str,
361        visited: &mut HashSet<String>,
362        visiting: &mut HashSet<String>,
363        order: &mut Vec<String>,
364    ) -> SklResult<()> {
365        if visiting.contains(component_id) {
366            return Err(SklearsError::InvalidInput(format!(
367                "Circular dependency detected involving {component_id}"
368            )));
369        }
370
371        if visited.contains(component_id) {
372            return Ok(());
373        }
374
375        visiting.insert(component_id.to_string());
376
377        if let Some(deps) = self.dependencies.get(component_id) {
378            for dep in deps {
379                self.topological_sort(dep, visited, visiting, order)?;
380            }
381        }
382
383        visiting.remove(component_id);
384        visited.insert(component_id.to_string());
385        order.push(component_id.to_string());
386
387        Ok(())
388    }
389
390    fn is_valid_transition(
391        &self,
392        from: &ComponentLifecycleState,
393        to: &ComponentLifecycleState,
394    ) -> bool {
395        use ComponentLifecycleState::{
396            Created, Error, Initializing, Paused, Ready, Running, Stopped, Stopping,
397        };
398
399        match (from, to) {
400            (Created, Initializing) => true,
401            (Initializing, Ready) => true,
402            (Initializing, Error(_)) => true,
403            (Ready, Running) => true,
404            (Ready, Paused) => true,
405            (Ready, Stopping) => true,
406            (Running, Paused) => true,
407            (Running, Stopping) => true,
408            (Paused, Running) => true,
409            (Paused, Stopping) => true,
410            (Stopping, Stopped) => true,
411            (Error(_), Initializing) => true, // Allow retry
412            _ => false,
413        }
414    }
415
416    fn notify_lifecycle_event(&mut self, event: LifecycleEvent) -> SklResult<()> {
417        if let Some(listeners) = self.listeners.get(&event) {
418            for listener in listeners {
419                // In real implementation, would notify actual listener
420                // For now, just track that notification would be sent
421                self.metrics.total_notifications += 1;
422            }
423        }
424        Ok(())
425    }
426}
427
428/// Component lifecycle state
429#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
430pub enum ComponentLifecycleState {
431    /// Component has been created but not initialized
432    Created,
433    /// Component is currently initializing
434    Initializing,
435    /// Component is ready for use
436    Ready,
437    /// Component is actively running
438    Running,
439    /// Component is paused
440    Paused,
441    /// Component is shutting down
442    Stopping,
443    /// Component has been stopped
444    Stopped,
445    /// Component encountered an error
446    Error(String),
447}
448
449/// Lifecycle events for monitoring and coordination
450#[derive(Debug, Clone, PartialEq, Eq, Hash)]
451pub enum LifecycleEvent {
452    StateChanged {
453        component_id: String,
454        old_state: ComponentLifecycleState,
455        new_state: ComponentLifecycleState,
456    },
457    /// Component was initialized
458    ComponentInitialized { component_id: String },
459    /// Component was shutdown
460    ComponentShutdown { component_id: String },
461    /// All components initialized
462    AllComponentsInitialized,
463    /// All components shutdown
464    AllComponentsShutdown,
465}
466
467/// Lifecycle configuration
468#[derive(Debug, Clone)]
469pub struct LifecycleConfig {
470    /// Fail fast on initialization errors
471    pub fail_fast_initialization: bool,
472    /// Maximum initialization timeout per component
473    pub initialization_timeout: Duration,
474    /// Maximum shutdown timeout per component
475    pub shutdown_timeout: Duration,
476    /// Enable automatic restart on failures
477    pub auto_restart_on_failure: bool,
478    /// Maximum restart attempts
479    pub max_restart_attempts: u32,
480}
481
482impl Default for LifecycleConfig {
483    fn default() -> Self {
484        Self {
485            fail_fast_initialization: true,
486            initialization_timeout: Duration::from_secs(30),
487            shutdown_timeout: Duration::from_secs(10),
488            auto_restart_on_failure: false,
489            max_restart_attempts: 3,
490        }
491    }
492}
493
494/// Lifecycle metrics for monitoring
495#[derive(Debug, Clone)]
496pub struct LifecycleMetrics {
497    /// Total number of components
498    pub total_components: usize,
499    /// Number of ready components
500    pub ready_components: usize,
501    /// Number of running components
502    pub running_components: usize,
503    /// Number of failed components
504    pub failed_components: usize,
505    /// Last initialization time
506    pub last_initialization_time: Duration,
507    /// Last shutdown time
508    pub last_shutdown_time: Duration,
509    /// Total lifecycle notifications sent
510    pub total_notifications: u64,
511}
512
513impl Default for LifecycleMetrics {
514    fn default() -> Self {
515        Self::new()
516    }
517}
518
519impl LifecycleMetrics {
520    #[must_use]
521    pub fn new() -> Self {
522        Self {
523            total_components: 0,
524            ready_components: 0,
525            running_components: 0,
526            failed_components: 0,
527            last_initialization_time: Duration::from_secs(0),
528            last_shutdown_time: Duration::from_secs(0),
529            total_notifications: 0,
530        }
531    }
532}
533
534/// Result of initialization process
535#[derive(Debug, Clone)]
536pub struct InitializationResult {
537    /// Total number of components
538    pub total_components: usize,
539    /// Number of successful initializations
540    pub successful_initializations: usize,
541    /// Number of failed initializations
542    pub failed_initializations: usize,
543    /// Order in which components were initialized
544    pub initialization_order: Vec<String>,
545    /// Total initialization duration
546    pub duration: Duration,
547    /// Initialization errors
548    pub errors: Vec<String>,
549}
550
551/// Result of shutdown process
552#[derive(Debug, Clone)]
553pub struct ShutdownResult {
554    /// Total number of components
555    pub total_components: usize,
556    /// Number of successful shutdowns
557    pub successful_shutdowns: usize,
558    /// Number of failed shutdowns
559    pub failed_shutdowns: usize,
560    /// Order in which components were shutdown
561    pub shutdown_order: Vec<String>,
562    /// Total shutdown duration
563    pub duration: Duration,
564    /// Shutdown errors
565    pub errors: Vec<String>,
566}
567
568impl Default for LifecycleManager {
569    fn default() -> Self {
570        Self::new()
571    }
572}
573
574#[allow(non_snake_case)]
575#[cfg(test)]
576mod tests {
577    use super::*;
578
579    #[test]
580    fn test_lifecycle_manager_creation() {
581        let manager = LifecycleManager::new();
582        assert_eq!(manager.get_metrics().total_components, 0);
583    }
584
585    #[test]
586    fn test_component_registration() {
587        let mut manager = LifecycleManager::new();
588
589        let result = manager.register_component("component_a", vec![]);
590        assert!(result.is_ok());
591        assert_eq!(manager.get_metrics().total_components, 1);
592
593        let state = manager.get_component_state("component_a");
594        assert_eq!(state, Some(&ComponentLifecycleState::Created));
595    }
596
597    #[test]
598    fn test_state_transitions() {
599        let mut manager = LifecycleManager::new();
600        manager
601            .register_component("component_a", vec![])
602            .unwrap_or_default();
603
604        // Valid transition
605        let result =
606            manager.set_component_state("component_a", ComponentLifecycleState::Initializing);
607        assert!(result.is_ok());
608
609        // Invalid transition
610        let result = manager.set_component_state("component_a", ComponentLifecycleState::Stopped);
611        assert!(result.is_err());
612    }
613
614    #[test]
615    fn test_dependency_resolution() {
616        let mut manager = LifecycleManager::new();
617
618        manager
619            .register_component("component_a", vec![])
620            .unwrap_or_default();
621        manager
622            .register_component("component_b", vec!["component_a".to_string()])
623            .unwrap_or_default();
624
625        let order = manager.calculate_initialization_order().unwrap_or_default();
626        assert_eq!(order, vec!["component_a", "component_b"]);
627    }
628
629    #[test]
630    fn test_circular_dependency_detection() {
631        let mut manager = LifecycleManager::new();
632
633        manager
634            .register_component("component_a", vec!["component_b".to_string()])
635            .unwrap_or_default();
636        manager
637            .register_component("component_b", vec!["component_a".to_string()])
638            .unwrap_or_default();
639
640        let result = manager.calculate_initialization_order();
641        assert!(result.is_err());
642    }
643
644    #[test]
645    fn test_component_initialization() {
646        let mut manager = LifecycleManager::new();
647        manager
648            .register_component("component_a", vec![])
649            .unwrap_or_default();
650
651        let result = manager.initialize_component("component_a");
652        assert!(result.is_ok());
653
654        let state = manager.get_component_state("component_a");
655        assert_eq!(state, Some(&ComponentLifecycleState::Ready));
656    }
657
658    #[test]
659    fn test_full_lifecycle() {
660        let mut manager = LifecycleManager::new();
661
662        manager
663            .register_component("component_a", vec![])
664            .unwrap_or_default();
665        manager
666            .register_component("component_b", vec!["component_a".to_string()])
667            .unwrap_or_default();
668
669        let init_result = manager
670            .initialize_all_components()
671            .expect("operation should succeed");
672        assert_eq!(init_result.successful_initializations, 2);
673        assert_eq!(init_result.failed_initializations, 0);
674
675        let shutdown_result = manager
676            .shutdown_all_components()
677            .expect("operation should succeed");
678        assert_eq!(shutdown_result.successful_shutdowns, 2);
679        assert_eq!(shutdown_result.failed_shutdowns, 0);
680    }
681}