oxirs_vec/real_time_embedding_pipeline/
consistency.rs1use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
6use std::sync::{Arc, RwLock};
7use std::time::{Duration, SystemTime};
8
9use crate::real_time_embedding_pipeline::{
10 config::ConsistencyLevel,
11 traits::{
12 ConsistencyRepairStrategy, HealthStatus, Inconsistency, InconsistencySeverity,
13 RepairResult, RepairStatus,
14 },
15};
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ConsistencyConfig {
20 pub consistency_level: ConsistencyLevel,
21 pub check_interval: Duration,
22 pub max_repair_attempts: usize,
23 pub enable_auto_repair: bool,
24}
25
26impl Default for ConsistencyConfig {
27 fn default() -> Self {
28 Self {
29 consistency_level: ConsistencyLevel::Session,
30 check_interval: Duration::from_secs(60),
31 max_repair_attempts: 3,
32 enable_auto_repair: true,
33 }
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct ConsistencyStatistics {
40 pub total_checks: u64,
41 pub total_inconsistencies_detected: u64,
42 pub total_repairs_attempted: u64,
43 pub total_repairs_succeeded: u64,
44 pub is_running: bool,
45}
46
47pub struct DefaultRepairStrategy;
49
50impl ConsistencyRepairStrategy for DefaultRepairStrategy {
51 fn repair_inconsistencies(
52 &self,
53 inconsistencies: &[Inconsistency],
54 ) -> Result<Vec<RepairResult>> {
55 let results = inconsistencies
56 .iter()
57 .map(|inc| {
58 let repaired_at = SystemTime::now();
59 let status = match inc.severity {
60 InconsistencySeverity::Low | InconsistencySeverity::Medium => {
61 RepairStatus::Success
62 }
63 InconsistencySeverity::High => RepairStatus::PartialSuccess,
64 InconsistencySeverity::Critical => RepairStatus::Skipped {
65 reason: "Critical inconsistency requires manual intervention".to_string(),
66 },
67 };
68 RepairResult {
69 inconsistency: inc.clone(),
70 status,
71 actions: vec!["logged".to_string(), "flagged_for_review".to_string()],
72 repaired_at,
73 }
74 })
75 .collect();
76 Ok(results)
77 }
78
79 fn get_strategy_name(&self) -> &str {
80 "default_repair_strategy"
81 }
82}
83
84pub struct InconsistencyRepairEngine {
86 strategies: Vec<Box<dyn ConsistencyRepairStrategy>>,
87 total_repairs: AtomicU64,
88}
89
90impl InconsistencyRepairEngine {
91 pub fn new() -> Self {
92 Self {
93 strategies: vec![Box::new(DefaultRepairStrategy)],
94 total_repairs: AtomicU64::new(0),
95 }
96 }
97
98 pub fn add_strategy(&mut self, strategy: Box<dyn ConsistencyRepairStrategy>) {
99 self.strategies.push(strategy);
100 }
101
102 pub fn repair(&self, inconsistencies: &[Inconsistency]) -> Result<Vec<RepairResult>> {
103 let mut all_results = Vec::new();
104 if let Some(strategy) = self.strategies.first() {
105 let results = strategy.repair_inconsistencies(inconsistencies)?;
106 let count = results.len() as u64;
107 all_results.extend(results);
108 self.total_repairs.fetch_add(count, Ordering::Relaxed);
109 }
110 Ok(all_results)
111 }
112
113 pub fn total_repairs(&self) -> u64 {
114 self.total_repairs.load(Ordering::Acquire)
115 }
116}
117
118impl Default for InconsistencyRepairEngine {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124pub struct ConsistencyManager {
126 config: ConsistencyConfig,
127 repair_engine: Arc<RwLock<InconsistencyRepairEngine>>,
128 is_running: AtomicBool,
129 total_checks: AtomicU64,
130 total_detected: AtomicU64,
131 total_repairs: AtomicU64,
132 total_success: AtomicU64,
133}
134
135impl ConsistencyManager {
136 pub fn new(consistency_level: ConsistencyLevel) -> Result<Self> {
137 let config = ConsistencyConfig {
138 consistency_level,
139 ..Default::default()
140 };
141 Ok(Self {
142 config,
143 repair_engine: Arc::new(RwLock::new(InconsistencyRepairEngine::new())),
144 is_running: AtomicBool::new(false),
145 total_checks: AtomicU64::new(0),
146 total_detected: AtomicU64::new(0),
147 total_repairs: AtomicU64::new(0),
148 total_success: AtomicU64::new(0),
149 })
150 }
151
152 pub async fn start_consistency_checking(&self) -> Result<()> {
153 self.is_running.store(true, Ordering::Release);
154 Ok(())
155 }
156
157 pub async fn stop(&self) -> Result<()> {
158 self.is_running.store(false, Ordering::Release);
159 Ok(())
160 }
161
162 pub async fn health_check(&self) -> Result<HealthStatus> {
163 if self.is_running.load(Ordering::Acquire) {
164 Ok(HealthStatus::Healthy)
165 } else {
166 Ok(HealthStatus::Warning {
167 message: "Consistency manager not running".to_string(),
168 })
169 }
170 }
171
172 pub fn detect_and_repair(
173 &self,
174 inconsistencies: Vec<Inconsistency>,
175 ) -> Result<Vec<RepairResult>> {
176 self.total_checks.fetch_add(1, Ordering::Relaxed);
177 self.total_detected
178 .fetch_add(inconsistencies.len() as u64, Ordering::Relaxed);
179
180 if !self.config.enable_auto_repair || inconsistencies.is_empty() {
181 return Ok(vec![]);
182 }
183
184 let engine = self
185 .repair_engine
186 .read()
187 .map_err(|_| anyhow::anyhow!("Failed to acquire repair engine lock"))?;
188
189 self.total_repairs
190 .fetch_add(inconsistencies.len() as u64, Ordering::Relaxed);
191 let results = engine.repair(&inconsistencies)?;
192
193 let successes = results
194 .iter()
195 .filter(|r| matches!(r.status, RepairStatus::Success))
196 .count();
197 self.total_success
198 .fetch_add(successes as u64, Ordering::Relaxed);
199
200 Ok(results)
201 }
202
203 pub fn get_statistics(&self) -> ConsistencyStatistics {
204 ConsistencyStatistics {
205 total_checks: self.total_checks.load(Ordering::Acquire),
206 total_inconsistencies_detected: self.total_detected.load(Ordering::Acquire),
207 total_repairs_attempted: self.total_repairs.load(Ordering::Acquire),
208 total_repairs_succeeded: self.total_success.load(Ordering::Acquire),
209 is_running: self.is_running.load(Ordering::Acquire),
210 }
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use crate::real_time_embedding_pipeline::config::ConsistencyLevel;
218 use crate::real_time_embedding_pipeline::traits::{
219 Inconsistency, InconsistencySeverity, InconsistencyType,
220 };
221
222 fn make_inconsistency(severity: InconsistencySeverity) -> Inconsistency {
223 Inconsistency {
224 inconsistency_type: InconsistencyType::DataMismatch,
225 affected_resources: vec!["resource1".to_string()],
226 description: "Test inconsistency".to_string(),
227 severity,
228 detected_at: SystemTime::now(),
229 }
230 }
231
232 #[tokio::test]
233 async fn test_consistency_manager_lifecycle() {
234 let manager = ConsistencyManager::new(ConsistencyLevel::Session).expect("should create");
235 manager
236 .start_consistency_checking()
237 .await
238 .expect("should start");
239 let health = manager.health_check().await.expect("should check");
240 assert!(matches!(health, HealthStatus::Healthy));
241 manager.stop().await.expect("should stop");
242 }
243
244 #[test]
245 fn test_default_repair_strategy() {
246 let strategy = DefaultRepairStrategy;
247 let inc = make_inconsistency(InconsistencySeverity::Low);
248 let results = strategy
249 .repair_inconsistencies(&[inc])
250 .expect("should repair");
251 assert_eq!(results.len(), 1);
252 assert!(matches!(results[0].status, RepairStatus::Success));
253 }
254
255 #[test]
256 fn test_critical_inconsistency_skipped() {
257 let strategy = DefaultRepairStrategy;
258 let inc = make_inconsistency(InconsistencySeverity::Critical);
259 let results = strategy
260 .repair_inconsistencies(&[inc])
261 .expect("should repair");
262 assert!(matches!(results[0].status, RepairStatus::Skipped { .. }));
263 }
264
265 #[test]
266 fn test_detect_and_repair() {
267 let manager = ConsistencyManager::new(ConsistencyLevel::Strong).expect("should create");
268 let incs = vec![make_inconsistency(InconsistencySeverity::Low)];
269 let results = manager.detect_and_repair(incs).expect("should work");
270 assert_eq!(results.len(), 1);
271 let stats = manager.get_statistics();
272 assert_eq!(stats.total_checks, 1);
273 assert_eq!(stats.total_inconsistencies_detected, 1);
274 }
275
276 #[test]
277 fn test_repair_engine() {
278 let engine = InconsistencyRepairEngine::new();
279 let inc = make_inconsistency(InconsistencySeverity::Medium);
280 let results = engine.repair(&[inc]).expect("should repair");
281 assert!(!results.is_empty());
282 }
283}