use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
use tracing::debug;
use uuid::Uuid;
use crate::real_time_embedding_pipeline::{
config::PipelineConfig,
consistency::ConsistencyManager,
coordination::UpdateCoordinator,
monitoring::{ConsoleAlertHandler, PipelinePerformanceMonitor},
streaming::{StreamConfig, StreamProcessor},
traits::{EmbeddingGenerator, IncrementalVectorIndex},
types::PipelineStatistics,
versioning::VersionManager,
PipelineError, PipelineResult,
};
pub struct RealTimeEmbeddingPipeline {
config: PipelineConfig,
embedding_generators: Arc<RwLock<HashMap<String, Box<dyn EmbeddingGenerator>>>>,
indices: Arc<RwLock<HashMap<String, Box<dyn IncrementalVectorIndex>>>>,
stream_processors: Arc<RwLock<HashMap<String, StreamProcessor>>>,
update_coordinator: Arc<UpdateCoordinator>,
performance_monitor: Arc<PipelinePerformanceMonitor>,
version_manager: Arc<VersionManager>,
consistency_manager: Arc<ConsistencyManager>,
is_running: AtomicBool,
stats: Arc<PipelineStatistics>,
}
impl RealTimeEmbeddingPipeline {
pub fn new(config: PipelineConfig) -> PipelineResult<Self> {
let embedding_generators = Arc::new(RwLock::new(HashMap::new()));
let indices = Arc::new(RwLock::new(HashMap::new()));
let stream_processors = Arc::new(RwLock::new(HashMap::new()));
let update_coordinator = Arc::new(UpdateCoordinator::new(&config).map_err(|e| {
PipelineError::ConfigurationError {
message: format!("Failed to create update coordinator: {}", e),
}
})?);
let alert_handler = Arc::new(ConsoleAlertHandler);
let performance_monitor = Arc::new(PipelinePerformanceMonitor::new(
config.monitoring_config.clone(),
alert_handler,
));
let version_manager = Arc::new(
VersionManager::new(config.version_control.clone()).map_err(|e| {
PipelineError::VersionError {
message: format!("Failed to create version manager: {}", e),
}
})?,
);
let consistency_manager = Arc::new(
ConsistencyManager::new(config.consistency_level.clone()).map_err(|e| {
PipelineError::ConsistencyError {
message: format!("Failed to create consistency manager: {}", e),
}
})?,
);
let stats = Arc::new(PipelineStatistics::default());
Ok(Self {
config,
embedding_generators,
indices,
stream_processors,
update_coordinator,
performance_monitor,
version_manager,
consistency_manager,
is_running: AtomicBool::new(false),
stats,
})
}
pub async fn start(&self) -> PipelineResult<()> {
if self.is_running.load(Ordering::Acquire) {
return Err(PipelineError::AlreadyRunning);
}
self.is_running.store(true, Ordering::Release);
self.performance_monitor
.start()
.await
.map_err(|e| PipelineError::MonitoringError {
message: format!("Failed to start performance monitor: {}", e),
})?;
self.start_update_coordinator().await?;
self.start_stream_processors().await?;
self.consistency_manager
.start_consistency_checking()
.await
.map_err(|e| PipelineError::ConsistencyError {
message: format!("Failed to start consistency checking: {}", e),
})?;
self.version_manager
.start()
.await
.map_err(|e| PipelineError::VersionError {
message: format!("Failed to start version manager: {}", e),
})?;
Ok(())
}
pub async fn stop(&self) -> PipelineResult<()> {
self.is_running.store(false, Ordering::Release);
let _ = self.performance_monitor.stop().await;
let _ = self.consistency_manager.stop().await;
let _ = self.version_manager.stop().await;
debug!("Stream processor stopping not yet implemented to avoid cloning issues");
Ok(())
}
pub fn add_embedding_generator(
&self,
name: String,
generator: Box<dyn EmbeddingGenerator>,
) -> PipelineResult<()> {
let mut generators =
self.embedding_generators
.write()
.map_err(|_| PipelineError::CoordinationError {
message: "Failed to acquire generators lock".to_string(),
})?;
generators.insert(name, generator);
Ok(())
}
pub fn add_vector_index(
&self,
name: String,
index: Box<dyn IncrementalVectorIndex>,
) -> PipelineResult<()> {
let mut indices = self
.indices
.write()
.map_err(|_| PipelineError::CoordinationError {
message: "Failed to acquire indices lock".to_string(),
})?;
indices.insert(name, index);
Ok(())
}
pub async fn create_stream(&self, config: StreamConfig) -> PipelineResult<String> {
let stream_id = Uuid::new_v4().to_string();
let processor = StreamProcessor::new(stream_id.clone(), config).map_err(|e| {
PipelineError::StreamProcessingError {
message: format!("Failed to create stream processor: {e}"),
}
})?;
{
let mut processors =
self.stream_processors
.write()
.map_err(|_| PipelineError::CoordinationError {
message: "Failed to acquire stream processors lock".to_string(),
})?;
processors.insert(stream_id.clone(), processor);
}
Ok(stream_id)
}
pub async fn remove_stream(&self, stream_id: &str) -> PipelineResult<()> {
let processor = {
let mut processors =
self.stream_processors
.write()
.map_err(|_| PipelineError::CoordinationError {
message: "Failed to acquire stream processors lock".to_string(),
})?;
processors.remove(stream_id)
};
if let Some(processor) = processor {
processor
.stop()
.await
.map_err(|e| PipelineError::StreamProcessingError {
message: format!("Failed to stop stream processor: {e}"),
})?;
}
Ok(())
}
pub fn get_statistics(&self) -> Arc<PipelineStatistics> {
self.stats.clone()
}
pub async fn health_check(
&self,
) -> PipelineResult<crate::real_time_embedding_pipeline::types::HealthCheckResult> {
let mut components = HashMap::new();
let monitor_health = self.performance_monitor.get_health_status().await?;
components.insert("performance_monitor".to_string(), monitor_health);
let consistency_health = self.consistency_manager.health_check().await.map_err(|e| {
PipelineError::ConsistencyError {
message: format!("Consistency manager health check failed: {}", e),
}
})?;
components.insert("consistency_manager".to_string(), consistency_health);
let version_health =
self.version_manager
.health_check()
.await
.map_err(|e| PipelineError::VersionError {
message: format!("Version manager health check failed: {}", e),
})?;
components.insert("version_manager".to_string(), version_health);
let processor_names: Vec<String> = {
let processors =
self.stream_processors
.read()
.map_err(|_| PipelineError::CoordinationError {
message: "Failed to acquire stream processors lock".to_string(),
})?;
processors.keys().cloned().collect()
};
for name in processor_names {
components.insert(
format!("stream_processor_{name}"),
crate::real_time_embedding_pipeline::traits::HealthStatus::Healthy,
);
}
let overall_status = if components.values().all(|status| {
matches!(
status,
crate::real_time_embedding_pipeline::traits::HealthStatus::Healthy
)
}) {
crate::real_time_embedding_pipeline::traits::HealthStatus::Healthy
} else if components.values().any(|status| {
matches!(
status,
crate::real_time_embedding_pipeline::traits::HealthStatus::Unhealthy { .. }
)
}) {
crate::real_time_embedding_pipeline::traits::HealthStatus::Unhealthy {
message: "One or more components are unhealthy".to_string(),
}
} else {
crate::real_time_embedding_pipeline::traits::HealthStatus::Warning {
message: "Some components have warnings".to_string(),
}
};
Ok(
crate::real_time_embedding_pipeline::types::HealthCheckResult {
status: overall_status,
components,
timestamp: std::time::SystemTime::now(),
details: HashMap::new(),
},
)
}
pub fn is_running(&self) -> bool {
self.is_running.load(Ordering::Acquire)
}
pub fn get_config(&self) -> &PipelineConfig {
&self.config
}
pub async fn update_config(&mut self, new_config: PipelineConfig) -> PipelineResult<()> {
if self.is_running() {
return Err(PipelineError::ConfigurationError {
message: "Cannot update configuration while pipeline is running".to_string(),
});
}
self.config = new_config;
Ok(())
}
pub fn list_embedding_generators(&self) -> PipelineResult<Vec<String>> {
let generators =
self.embedding_generators
.read()
.map_err(|_| PipelineError::CoordinationError {
message: "Failed to acquire generators lock".to_string(),
})?;
Ok(generators.keys().cloned().collect())
}
pub fn list_vector_indices(&self) -> PipelineResult<Vec<String>> {
let indices = self
.indices
.read()
.map_err(|_| PipelineError::CoordinationError {
message: "Failed to acquire indices lock".to_string(),
})?;
Ok(indices.keys().cloned().collect())
}
pub fn list_streams(&self) -> PipelineResult<Vec<String>> {
let processors =
self.stream_processors
.read()
.map_err(|_| PipelineError::CoordinationError {
message: "Failed to acquire stream processors lock".to_string(),
})?;
Ok(processors.keys().cloned().collect())
}
async fn start_update_coordinator(&self) -> PipelineResult<()> {
self.update_coordinator
.start()
.await
.map_err(|e| PipelineError::CoordinationError {
message: format!("Failed to start update coordinator: {}", e),
})
}
async fn start_stream_processors(&self) -> PipelineResult<()> {
debug!("Stream processors start not yet implemented to avoid mutex await issue");
Ok(())
}
}
impl Drop for RealTimeEmbeddingPipeline {
fn drop(&mut self) {
if self.is_running.load(Ordering::Acquire) {
self.is_running.store(false, Ordering::Release);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::real_time_embedding_pipeline::config::ConsistencyLevel;
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[tokio::test]
async fn test_pipeline_creation() {
let config = PipelineConfig::default();
let pipeline = RealTimeEmbeddingPipeline::new(config);
assert!(pipeline.is_ok());
}
#[tokio::test]
async fn test_pipeline_start_stop() -> Result<()> {
let config = PipelineConfig::default();
let pipeline = RealTimeEmbeddingPipeline::new(config)?;
assert!(!pipeline.is_running());
let start_result = pipeline.start().await;
let _ = start_result;
let stop_result = pipeline.stop().await;
let _ = stop_result;
Ok(())
}
#[test]
fn test_pipeline_configuration() -> Result<()> {
let config = PipelineConfig {
consistency_level: ConsistencyLevel::Strong,
max_batch_size: 500,
..Default::default()
};
let pipeline = RealTimeEmbeddingPipeline::new(config)?;
assert_eq!(pipeline.get_config().max_batch_size, 500);
assert_eq!(
pipeline.get_config().consistency_level,
ConsistencyLevel::Strong
);
Ok(())
}
}