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