omega_runtime/
degradation.rs

1//! Feature Degradation Management
2//!
3//! Provides graceful degradation of features when subsystems fail,
4//! with fallback mechanisms and feature flag management.
5
6use parking_lot::RwLock;
7use std::collections::{HashMap, HashSet};
8use std::sync::Arc;
9use thiserror::Error;
10use tracing::{debug, info, warn};
11
12/// Degradation error types
13#[derive(Debug, Error)]
14pub enum DegradationError {
15    #[error("Feature not found: {0}")]
16    FeatureNotFound(String),
17    #[error("Feature already registered: {0}")]
18    FeatureAlreadyRegistered(String),
19    #[error("Fallback execution failed: {0}")]
20    FallbackFailed(String),
21    #[error("Feature is disabled: {0}")]
22    FeatureDisabled(String),
23}
24
25/// Fallback function type
26pub type FallbackFn = Arc<dyn Fn() -> Result<(), Box<dyn std::error::Error + Send + Sync>> + Send + Sync>;
27
28/// Feature metadata
29#[derive(Clone)]
30pub struct FeatureInfo {
31    /// Feature name
32    pub name: String,
33    /// Whether feature is currently enabled
34    pub enabled: bool,
35    /// Number of times feature has been disabled
36    pub disable_count: u32,
37    /// Number of times feature has been re-enabled
38    pub enable_count: u32,
39    /// Number of times fallback has been executed
40    pub fallback_executions: u32,
41    /// Optional description
42    pub description: Option<String>,
43}
44
45impl FeatureInfo {
46    /// Create new feature info
47    pub fn new(name: String) -> Self {
48        Self {
49            name,
50            enabled: true,
51            disable_count: 0,
52            enable_count: 0,
53            fallback_executions: 0,
54            description: None,
55        }
56    }
57
58    /// Set description
59    pub fn with_description(mut self, description: String) -> Self {
60        self.description = Some(description);
61        self
62    }
63}
64
65/// Degradation manager for feature flags and fallback management
66pub struct DegradationManager {
67    /// Set of disabled features
68    disabled_features: RwLock<HashSet<String>>,
69    /// Map of features to fallback functions
70    fallbacks: RwLock<HashMap<String, FallbackFn>>,
71    /// Feature metadata and statistics
72    features: RwLock<HashMap<String, FeatureInfo>>,
73    /// Feature dependencies (features that depend on others)
74    dependencies: RwLock<HashMap<String, Vec<String>>>,
75}
76
77impl DegradationManager {
78    /// Create a new degradation manager
79    pub fn new() -> Self {
80        Self {
81            disabled_features: RwLock::new(HashSet::new()),
82            fallbacks: RwLock::new(HashMap::new()),
83            features: RwLock::new(HashMap::new()),
84            dependencies: RwLock::new(HashMap::new()),
85        }
86    }
87
88    /// Register a new feature
89    pub fn register_feature(&self, name: String) -> Result<(), DegradationError> {
90        let mut features = self.features.write();
91        if features.contains_key(&name) {
92            return Err(DegradationError::FeatureAlreadyRegistered(name));
93        }
94        features.insert(name.clone(), FeatureInfo::new(name.clone()));
95        debug!("Registered feature: {}", name);
96        Ok(())
97    }
98
99    /// Register a feature with description
100    pub fn register_feature_with_description(
101        &self,
102        name: String,
103        description: String,
104    ) -> Result<(), DegradationError> {
105        let mut features = self.features.write();
106        if features.contains_key(&name) {
107            return Err(DegradationError::FeatureAlreadyRegistered(name));
108        }
109        features.insert(
110            name.clone(),
111            FeatureInfo::new(name.clone()).with_description(description),
112        );
113        debug!("Registered feature with description: {}", name);
114        Ok(())
115    }
116
117    /// Disable a feature
118    pub fn disable_feature(&self, name: &str) -> Result<(), DegradationError> {
119        // Check if feature exists
120        let mut features = self.features.write();
121        let feature = features
122            .get_mut(name)
123            .ok_or_else(|| DegradationError::FeatureNotFound(name.to_string()))?;
124
125        if feature.enabled {
126            feature.enabled = false;
127            feature.disable_count += 1;
128            drop(features);
129
130            self.disabled_features.write().insert(name.to_string());
131            warn!("Disabled feature: {}", name);
132
133            // Disable dependent features
134            self.disable_dependents(name)?;
135        }
136
137        Ok(())
138    }
139
140    /// Enable a feature
141    pub fn enable_feature(&self, name: &str) -> Result<(), DegradationError> {
142        // Check if feature exists
143        let mut features = self.features.write();
144        let feature = features
145            .get_mut(name)
146            .ok_or_else(|| DegradationError::FeatureNotFound(name.to_string()))?;
147
148        if !feature.enabled {
149            feature.enabled = true;
150            feature.enable_count += 1;
151            drop(features);
152
153            self.disabled_features.write().remove(name);
154            info!("Enabled feature: {}", name);
155        }
156
157        Ok(())
158    }
159
160    /// Check if a feature is enabled
161    pub fn is_enabled(&self, name: &str) -> bool {
162        !self.disabled_features.read().contains(name)
163    }
164
165    /// Register a fallback function for a feature
166    pub fn register_fallback<F>(&self, name: String, fallback: F) -> Result<(), DegradationError>
167    where
168        F: Fn() -> Result<(), Box<dyn std::error::Error + Send + Sync>> + Send + Sync + 'static,
169    {
170        // Ensure feature is registered
171        if !self.features.read().contains_key(&name) {
172            self.register_feature(name.clone())?;
173        }
174
175        self.fallbacks
176            .write()
177            .insert(name.clone(), Arc::new(fallback));
178        debug!("Registered fallback for feature: {}", name);
179        Ok(())
180    }
181
182    /// Execute fallback for a feature
183    pub fn execute_fallback(&self, name: &str) -> Result<(), DegradationError> {
184        // Update statistics
185        if let Some(feature) = self.features.write().get_mut(name) {
186            feature.fallback_executions += 1;
187        }
188
189        let fallbacks = self.fallbacks.read();
190        let fallback = fallbacks
191            .get(name)
192            .ok_or_else(|| DegradationError::FeatureNotFound(name.to_string()))?;
193
194        debug!("Executing fallback for feature: {}", name);
195
196        fallback().map_err(|e| DegradationError::FallbackFailed(e.to_string()))
197    }
198
199    /// Execute operation with feature check and fallback
200    pub fn execute_with_fallback<F, T>(
201        &self,
202        feature_name: &str,
203        operation: F,
204    ) -> Result<T, DegradationError>
205    where
206        F: FnOnce() -> Result<T, Box<dyn std::error::Error + Send + Sync>>,
207        T: Default,
208    {
209        if self.is_enabled(feature_name) {
210            match operation() {
211                Ok(result) => Ok(result),
212                Err(e) => {
213                    warn!("Operation failed for feature {}: {}", feature_name, e);
214                    self.execute_fallback(feature_name)?;
215                    Ok(T::default())
216                }
217            }
218        } else {
219            self.execute_fallback(feature_name)?;
220            Ok(T::default())
221        }
222    }
223
224    /// Register feature dependency
225    pub fn add_dependency(
226        &self,
227        feature: String,
228        depends_on: String,
229    ) -> Result<(), DegradationError> {
230        // Ensure both features exist
231        if !self.features.read().contains_key(&feature) {
232            return Err(DegradationError::FeatureNotFound(feature));
233        }
234        if !self.features.read().contains_key(&depends_on) {
235            return Err(DegradationError::FeatureNotFound(depends_on));
236        }
237
238        self.dependencies
239            .write()
240            .entry(depends_on.clone())
241            .or_default()
242            .push(feature.clone());
243
244        debug!("Added dependency: {} depends on {}", feature, depends_on);
245        Ok(())
246    }
247
248    /// Disable all features that depend on a given feature
249    fn disable_dependents(&self, feature: &str) -> Result<(), DegradationError> {
250        let dependencies = self.dependencies.read();
251        if let Some(dependents) = dependencies.get(feature) {
252            let dependents_clone = dependents.clone();
253            drop(dependencies);
254
255            for dependent in dependents_clone {
256                warn!(
257                    "Disabling dependent feature {} because {} is disabled",
258                    dependent, feature
259                );
260                self.disable_feature(&dependent)?;
261            }
262        }
263
264        Ok(())
265    }
266
267    /// Get feature information
268    pub fn feature_info(&self, name: &str) -> Result<FeatureInfo, DegradationError> {
269        self.features
270            .read()
271            .get(name)
272            .cloned()
273            .ok_or_else(|| DegradationError::FeatureNotFound(name.to_string()))
274    }
275
276    /// Get all features
277    pub fn all_features(&self) -> HashMap<String, FeatureInfo> {
278        self.features.read().clone()
279    }
280
281    /// Get disabled features
282    pub fn disabled_features(&self) -> Vec<String> {
283        self.disabled_features.read().iter().cloned().collect()
284    }
285
286    /// Get enabled features
287    pub fn enabled_features(&self) -> Vec<String> {
288        let disabled = self.disabled_features.read();
289        self.features
290            .read()
291            .keys()
292            .filter(|k| !disabled.contains(*k))
293            .cloned()
294            .collect()
295    }
296
297    /// Get count of registered features
298    pub fn feature_count(&self) -> usize {
299        self.features.read().len()
300    }
301
302    /// Get count of disabled features
303    pub fn disabled_count(&self) -> usize {
304        self.disabled_features.read().len()
305    }
306
307    /// Clear all features and fallbacks
308    pub fn clear(&self) {
309        self.features.write().clear();
310        self.disabled_features.write().clear();
311        self.fallbacks.write().clear();
312        self.dependencies.write().clear();
313    }
314
315    /// Batch disable multiple features
316    pub fn batch_disable(&self, features: &[&str]) -> Result<(), DegradationError> {
317        for feature in features {
318            self.disable_feature(feature)?;
319        }
320        Ok(())
321    }
322
323    /// Batch enable multiple features
324    pub fn batch_enable(&self, features: &[&str]) -> Result<(), DegradationError> {
325        for feature in features {
326            self.enable_feature(feature)?;
327        }
328        Ok(())
329    }
330}
331
332impl Default for DegradationManager {
333    fn default() -> Self {
334        Self::new()
335    }
336}
337
338#[cfg(test)]
339mod tests {
340    use super::*;
341    use std::sync::atomic::{AtomicU32, Ordering};
342
343    #[test]
344    fn test_degradation_manager_creation() {
345        let manager = DegradationManager::new();
346        assert_eq!(manager.feature_count(), 0);
347        assert_eq!(manager.disabled_count(), 0);
348    }
349
350    #[test]
351    fn test_register_feature() {
352        let manager = DegradationManager::new();
353        manager.register_feature("test-feature".to_string()).unwrap();
354        assert_eq!(manager.feature_count(), 1);
355        assert!(manager.is_enabled("test-feature"));
356    }
357
358    #[test]
359    fn test_register_duplicate_feature() {
360        let manager = DegradationManager::new();
361        manager.register_feature("test".to_string()).unwrap();
362        let result = manager.register_feature("test".to_string());
363        assert!(result.is_err());
364    }
365
366    #[test]
367    fn test_disable_feature() {
368        let manager = DegradationManager::new();
369        manager.register_feature("test".to_string()).unwrap();
370
371        assert!(manager.is_enabled("test"));
372        manager.disable_feature("test").unwrap();
373        assert!(!manager.is_enabled("test"));
374        assert_eq!(manager.disabled_count(), 1);
375    }
376
377    #[test]
378    fn test_enable_feature() {
379        let manager = DegradationManager::new();
380        manager.register_feature("test".to_string()).unwrap();
381
382        manager.disable_feature("test").unwrap();
383        assert!(!manager.is_enabled("test"));
384
385        manager.enable_feature("test").unwrap();
386        assert!(manager.is_enabled("test"));
387        assert_eq!(manager.disabled_count(), 0);
388    }
389
390    #[test]
391    fn test_disable_nonexistent_feature() {
392        let manager = DegradationManager::new();
393        let result = manager.disable_feature("nonexistent");
394        assert!(result.is_err());
395    }
396
397    #[test]
398    fn test_register_fallback() {
399        let manager = DegradationManager::new();
400        manager.register_feature("test".to_string()).unwrap();
401
402        let result = manager.register_fallback("test".to_string(), || Ok(()));
403        assert!(result.is_ok());
404    }
405
406    #[test]
407    fn test_execute_fallback() {
408        let manager = DegradationManager::new();
409        manager.register_feature("test".to_string()).unwrap();
410
411        let counter = Arc::new(AtomicU32::new(0));
412        let counter_clone = Arc::clone(&counter);
413
414        manager
415            .register_fallback("test".to_string(), move || {
416                counter_clone.fetch_add(1, Ordering::SeqCst);
417                Ok(())
418            })
419            .unwrap();
420
421        manager.execute_fallback("test").unwrap();
422        assert_eq!(counter.load(Ordering::SeqCst), 1);
423    }
424
425    #[test]
426    fn test_execute_fallback_failure() {
427        let manager = DegradationManager::new();
428        manager.register_feature("test".to_string()).unwrap();
429
430        manager
431            .register_fallback("test".to_string(), || {
432                Err("fallback error".into())
433            })
434            .unwrap();
435
436        let result = manager.execute_fallback("test");
437        assert!(result.is_err());
438    }
439
440    #[test]
441    fn test_execute_with_fallback_success() {
442        let manager = DegradationManager::new();
443        manager.register_feature("test".to_string()).unwrap();
444
445        let counter = Arc::new(AtomicU32::new(0));
446        let counter_clone = Arc::clone(&counter);
447
448        manager
449            .register_fallback("test".to_string(), move || {
450                counter_clone.fetch_add(1, Ordering::SeqCst);
451                Ok(())
452            })
453            .unwrap();
454
455        let result: Result<i32, DegradationError> =
456            manager.execute_with_fallback("test", || Ok(42));
457
458        assert_eq!(result.unwrap(), 42);
459        assert_eq!(counter.load(Ordering::SeqCst), 0); // Fallback not called
460    }
461
462    #[test]
463    fn test_execute_with_fallback_on_failure() {
464        let manager = DegradationManager::new();
465        manager.register_feature("test".to_string()).unwrap();
466
467        let counter = Arc::new(AtomicU32::new(0));
468        let counter_clone = Arc::clone(&counter);
469
470        manager
471            .register_fallback("test".to_string(), move || {
472                counter_clone.fetch_add(1, Ordering::SeqCst);
473                Ok(())
474            })
475            .unwrap();
476
477        let result: Result<i32, DegradationError> =
478            manager.execute_with_fallback("test", || Err("operation failed".into()));
479
480        assert_eq!(result.unwrap(), 0); // Default value
481        assert_eq!(counter.load(Ordering::SeqCst), 1); // Fallback called
482    }
483
484    #[test]
485    fn test_execute_with_fallback_when_disabled() {
486        let manager = DegradationManager::new();
487        manager.register_feature("test".to_string()).unwrap();
488
489        let counter = Arc::new(AtomicU32::new(0));
490        let counter_clone = Arc::clone(&counter);
491
492        manager
493            .register_fallback("test".to_string(), move || {
494                counter_clone.fetch_add(1, Ordering::SeqCst);
495                Ok(())
496            })
497            .unwrap();
498
499        manager.disable_feature("test").unwrap();
500
501        let result: Result<i32, DegradationError> =
502            manager.execute_with_fallback("test", || Ok(42));
503
504        assert_eq!(result.unwrap(), 0); // Default value
505        assert_eq!(counter.load(Ordering::SeqCst), 1); // Fallback called
506    }
507
508    #[test]
509    fn test_feature_dependencies() {
510        let manager = DegradationManager::new();
511        manager.register_feature("parent".to_string()).unwrap();
512        manager.register_feature("child".to_string()).unwrap();
513
514        manager
515            .add_dependency("child".to_string(), "parent".to_string())
516            .unwrap();
517
518        assert!(manager.is_enabled("parent"));
519        assert!(manager.is_enabled("child"));
520
521        // Disabling parent should disable child
522        manager.disable_feature("parent").unwrap();
523        assert!(!manager.is_enabled("parent"));
524        assert!(!manager.is_enabled("child"));
525    }
526
527    #[test]
528    fn test_feature_info() {
529        let manager = DegradationManager::new();
530        manager.register_feature("test".to_string()).unwrap();
531
532        let info = manager.feature_info("test").unwrap();
533        assert_eq!(info.name, "test");
534        assert!(info.enabled);
535        assert_eq!(info.disable_count, 0);
536    }
537
538    #[test]
539    fn test_feature_statistics() {
540        let manager = DegradationManager::new();
541        manager.register_feature("test".to_string()).unwrap();
542        manager.register_fallback("test".to_string(), || Ok(())).unwrap();
543
544        manager.disable_feature("test").unwrap();
545        manager.enable_feature("test").unwrap();
546        manager.disable_feature("test").unwrap();
547        manager.execute_fallback("test").unwrap();
548
549        let info = manager.feature_info("test").unwrap();
550        assert_eq!(info.disable_count, 2);
551        assert_eq!(info.enable_count, 1);
552        assert_eq!(info.fallback_executions, 1);
553    }
554
555    #[test]
556    fn test_disabled_features_list() {
557        let manager = DegradationManager::new();
558        manager.register_feature("feature1".to_string()).unwrap();
559        manager.register_feature("feature2".to_string()).unwrap();
560        manager.register_feature("feature3".to_string()).unwrap();
561
562        manager.disable_feature("feature1").unwrap();
563        manager.disable_feature("feature3").unwrap();
564
565        let disabled = manager.disabled_features();
566        assert_eq!(disabled.len(), 2);
567        assert!(disabled.contains(&"feature1".to_string()));
568        assert!(disabled.contains(&"feature3".to_string()));
569    }
570
571    #[test]
572    fn test_enabled_features_list() {
573        let manager = DegradationManager::new();
574        manager.register_feature("feature1".to_string()).unwrap();
575        manager.register_feature("feature2".to_string()).unwrap();
576        manager.register_feature("feature3".to_string()).unwrap();
577
578        manager.disable_feature("feature1").unwrap();
579
580        let enabled = manager.enabled_features();
581        assert_eq!(enabled.len(), 2);
582        assert!(enabled.contains(&"feature2".to_string()));
583        assert!(enabled.contains(&"feature3".to_string()));
584    }
585
586    #[test]
587    fn test_clear() {
588        let manager = DegradationManager::new();
589        manager.register_feature("test1".to_string()).unwrap();
590        manager.register_feature("test2".to_string()).unwrap();
591        manager.disable_feature("test1").unwrap();
592
593        assert_eq!(manager.feature_count(), 2);
594        assert_eq!(manager.disabled_count(), 1);
595
596        manager.clear();
597
598        assert_eq!(manager.feature_count(), 0);
599        assert_eq!(manager.disabled_count(), 0);
600    }
601
602    #[test]
603    fn test_batch_disable() {
604        let manager = DegradationManager::new();
605        manager.register_feature("f1".to_string()).unwrap();
606        manager.register_feature("f2".to_string()).unwrap();
607        manager.register_feature("f3".to_string()).unwrap();
608
609        manager.batch_disable(&["f1", "f2"]).unwrap();
610
611        assert!(!manager.is_enabled("f1"));
612        assert!(!manager.is_enabled("f2"));
613        assert!(manager.is_enabled("f3"));
614        assert_eq!(manager.disabled_count(), 2);
615    }
616
617    #[test]
618    fn test_batch_enable() {
619        let manager = DegradationManager::new();
620        manager.register_feature("f1".to_string()).unwrap();
621        manager.register_feature("f2".to_string()).unwrap();
622        manager.disable_feature("f1").unwrap();
623        manager.disable_feature("f2").unwrap();
624
625        manager.batch_enable(&["f1", "f2"]).unwrap();
626
627        assert!(manager.is_enabled("f1"));
628        assert!(manager.is_enabled("f2"));
629        assert_eq!(manager.disabled_count(), 0);
630    }
631
632    #[test]
633    fn test_feature_with_description() {
634        let manager = DegradationManager::new();
635        manager
636            .register_feature_with_description(
637                "test".to_string(),
638                "Test feature description".to_string(),
639            )
640            .unwrap();
641
642        let info = manager.feature_info("test").unwrap();
643        assert_eq!(
644            info.description,
645            Some("Test feature description".to_string())
646        );
647    }
648
649    #[test]
650    fn test_multiple_dependencies() {
651        let manager = DegradationManager::new();
652        manager.register_feature("parent".to_string()).unwrap();
653        manager.register_feature("child1".to_string()).unwrap();
654        manager.register_feature("child2".to_string()).unwrap();
655
656        manager
657            .add_dependency("child1".to_string(), "parent".to_string())
658            .unwrap();
659        manager
660            .add_dependency("child2".to_string(), "parent".to_string())
661            .unwrap();
662
663        manager.disable_feature("parent").unwrap();
664
665        assert!(!manager.is_enabled("child1"));
666        assert!(!manager.is_enabled("child2"));
667    }
668}