use std::collections::HashMap;
use std::sync::{ Arc, Mutex };
use std::sync::atomic::{ AtomicU64, AtomicUsize, Ordering };
use std::time::{ Duration, SystemTime };
#[ derive( Debug, Clone ) ]
pub struct DeploymentHealthCheckConfig
{
pub endpoint : String,
pub interval : Duration,
pub timeout : Duration,
pub failure_threshold : usize,
pub success_threshold : usize,
}
impl Default for DeploymentHealthCheckConfig
{
fn default() -> Self
{
Self {
endpoint : "/health".to_string(),
interval : Duration::from_secs( 30 ),
timeout : Duration::from_secs( 5 ),
failure_threshold : 3,
success_threshold : 1,
}
}
}
#[ derive( Debug, Clone ) ]
pub struct DeploymentHealthCheckConfigBuilder
{
config : DeploymentHealthCheckConfig,
}
impl DeploymentHealthCheckConfigBuilder
{
pub fn new() -> Self
{
Self {
config : DeploymentHealthCheckConfig::default(),
}
}
pub fn endpoint( mut self, endpoint : &str ) -> Self
{
self.config.endpoint = endpoint.to_string();
self
}
pub fn interval( mut self, interval : Duration ) -> Self
{
self.config.interval = interval;
self
}
pub fn timeout( mut self, timeout : Duration ) -> Self
{
self.config.timeout = timeout;
self
}
pub fn failure_threshold( mut self, threshold : usize ) -> Self
{
self.config.failure_threshold = threshold;
self
}
pub fn success_threshold( mut self, threshold : usize ) -> Self
{
self.config.success_threshold = threshold;
self
}
pub fn build( self ) -> Result< DeploymentHealthCheckConfig, crate::error::Error >
{
if self.config.endpoint.is_empty()
{
return Err( crate::error::Error::ConfigurationError(
"Health check endpoint cannot be empty".to_string()
) );
}
Ok( self.config )
}
}
impl DeploymentHealthCheckConfig
{
pub fn builder() -> DeploymentHealthCheckConfigBuilder
{
DeploymentHealthCheckConfigBuilder::new()
}
}
#[ derive( Debug, Clone ) ]
pub struct MonitoringConfig
{
pub enable_metrics : bool,
pub metrics_interval : Duration,
pub enable_logging : bool,
pub log_level : String,
pub alert_on_errors : bool,
pub metric_labels : HashMap< String, String >,
}
impl Default for MonitoringConfig
{
fn default() -> Self
{
Self {
enable_metrics : true,
metrics_interval : Duration::from_secs( 60 ),
enable_logging : true,
log_level : "INFO".to_string(),
alert_on_errors : true,
metric_labels : HashMap::new(),
}
}
}
#[ derive( Debug, Clone ) ]
pub struct MonitoringConfigBuilder
{
config : MonitoringConfig,
}
impl MonitoringConfigBuilder
{
pub fn new() -> Self
{
Self {
config : MonitoringConfig::default(),
}
}
pub fn enable_metrics( mut self, enable : bool ) -> Self
{
self.config.enable_metrics = enable;
self
}
pub fn metrics_interval( mut self, interval : Duration ) -> Self
{
self.config.metrics_interval = interval;
self
}
pub fn enable_logging( mut self, enable : bool ) -> Self
{
self.config.enable_logging = enable;
self
}
pub fn log_level( mut self, level : String ) -> Self
{
self.config.log_level = level;
self
}
pub fn alert_on_errors( mut self, alert : bool ) -> Self
{
self.config.alert_on_errors = alert;
self
}
pub fn metric_labels( mut self, labels : HashMap< String, String > ) -> Self
{
self.config.metric_labels = labels;
self
}
pub fn build( self ) -> Result< MonitoringConfig, crate::error::Error >
{
Ok( self.config )
}
}
impl MonitoringConfig
{
pub fn builder() -> MonitoringConfigBuilder
{
MonitoringConfigBuilder::new()
}
}
#[ derive( Debug ) ]
pub struct DeploymentMetrics
{
pub instance_count : AtomicUsize,
cpu_utilization_scaled : AtomicU64,
memory_utilization_scaled : AtomicU64,
request_rate_scaled : AtomicU64,
error_rate_scaled : AtomicU64,
pub response_time_us : AtomicU64,
uptime_percentage_scaled : AtomicU64,
pub total_requests : AtomicU64,
pub total_errors : AtomicU64,
pub last_updated_us : AtomicU64,
deployment_start_us : AtomicU64,
}
impl DeploymentMetrics
{
pub fn new() -> Self
{
let now_us = SystemTime::now()
.duration_since( SystemTime::UNIX_EPOCH )
.unwrap_or_default()
.as_micros() as u64;
Self {
instance_count : AtomicUsize::new( 0 ),
cpu_utilization_scaled : AtomicU64::new( 0 ),
memory_utilization_scaled : AtomicU64::new( 0 ),
request_rate_scaled : AtomicU64::new( 0 ),
error_rate_scaled : AtomicU64::new( 0 ),
response_time_us : AtomicU64::new( 0 ),
uptime_percentage_scaled : AtomicU64::new( 10000 ), total_requests : AtomicU64::new( 0 ),
total_errors : AtomicU64::new( 0 ),
last_updated_us : AtomicU64::new( now_us ),
deployment_start_us : AtomicU64::new( now_us ),
}
}
pub fn cpu_utilization( &self ) -> f64
{
self.cpu_utilization_scaled.load( Ordering::Relaxed ) as f64 / 100.0
}
pub fn set_cpu_utilization( &self, value : f64 )
{
let scaled = ( value * 100.0 ).round() as u64;
self.cpu_utilization_scaled.store( scaled, Ordering::Relaxed );
self.update_timestamp();
}
pub fn memory_utilization( &self ) -> f64
{
self.memory_utilization_scaled.load( Ordering::Relaxed ) as f64 / 100.0
}
pub fn set_memory_utilization( &self, value : f64 )
{
let scaled = ( value * 100.0 ).round() as u64;
self.memory_utilization_scaled.store( scaled, Ordering::Relaxed );
self.update_timestamp();
}
pub fn request_rate( &self ) -> f64
{
self.request_rate_scaled.load( Ordering::Relaxed ) as f64 / 100.0
}
pub fn set_request_rate( &self, value : f64 )
{
let scaled = ( value * 100.0 ).round() as u64;
self.request_rate_scaled.store( scaled, Ordering::Relaxed );
self.update_timestamp();
}
pub fn error_rate( &self ) -> f64
{
self.error_rate_scaled.load( Ordering::Relaxed ) as f64 / 100.0
}
pub fn record_request( &self, response_time_us : u64, is_error : bool )
{
self.total_requests.fetch_add( 1, Ordering::Relaxed );
if is_error
{
self.total_errors.fetch_add( 1, Ordering::Relaxed );
}
let current_avg = self.response_time_us.load( Ordering::Relaxed );
let new_avg = if current_avg == 0
{
response_time_us
} else {
( ( current_avg as f64 * 0.9 ) + ( response_time_us as f64 * 0.1 ) ).round() as u64
};
self.response_time_us.store( new_avg, Ordering::Relaxed );
let total_requests = self.total_requests.load( Ordering::Relaxed );
let total_errors = self.total_errors.load( Ordering::Relaxed );
let error_rate = if total_requests > 0
{
( total_errors as f64 / total_requests as f64 ) * 100.0
} else {
0.0
};
let error_rate_scaled = ( error_rate * 100.0 ).round() as u64;
self.error_rate_scaled.store( error_rate_scaled, Ordering::Relaxed );
self.update_timestamp();
}
pub fn uptime_percentage( &self ) -> f64
{
self.uptime_percentage_scaled.load( Ordering::Relaxed ) as f64 / 100.0
}
pub fn update_uptime( &self, is_healthy : bool )
{
let now_us = SystemTime::now()
.duration_since( SystemTime::UNIX_EPOCH )
.unwrap_or_default()
.as_micros() as u64;
let start_us = self.deployment_start_us.load( Ordering::Relaxed );
let total_time_us = now_us.saturating_sub( start_us );
if total_time_us > 0
{
let uptime_percentage = if is_healthy { 100.0 } else { 95.0 };
let uptime_scaled = ( uptime_percentage * 100.0_f64 ).round() as u64;
self.uptime_percentage_scaled.store( uptime_scaled, Ordering::Relaxed );
}
self.update_timestamp();
}
fn update_timestamp( &self )
{
let now_us = SystemTime::now()
.duration_since( SystemTime::UNIX_EPOCH )
.unwrap_or_default()
.as_micros() as u64;
self.last_updated_us.store( now_us, Ordering::Relaxed );
}
pub fn response_time_ms( &self ) -> f64
{
self.response_time_us.load( Ordering::Relaxed ) as f64 / 1000.0
}
pub fn to_prometheus( &self, deployment_id : &str ) -> String
{
format!(
"# HELP deployment_instance_count Number of active instances\n\
# TYPE deployment_instance_count gauge\n\
deployment_instance_count{{deployment_id=\"{}\"}} {}\n\
\n\
# HELP deployment_cpu_utilization CPU utilization percentage\n\
# TYPE deployment_cpu_utilization gauge\n\
deployment_cpu_utilization{{deployment_id=\"{}\"}} {:.2}\n\
\n\
# HELP deployment_memory_utilization Memory utilization percentage\n\
# TYPE deployment_memory_utilization gauge\n\
deployment_memory_utilization{{deployment_id=\"{}\"}} {:.2}\n\
\n\
# HELP deployment_request_rate Requests per second\n\
# TYPE deployment_request_rate gauge\n\
deployment_request_rate{{deployment_id=\"{}\"}} {:.2}\n\
\n\
# HELP deployment_error_rate Error rate percentage\n\
# TYPE deployment_error_rate gauge\n\
deployment_error_rate{{deployment_id=\"{}\"}} {:.2}\n\
\n\
# HELP deployment_response_time_ms Average response time in milliseconds\n\
# TYPE deployment_response_time_ms gauge\n\
deployment_response_time_ms{{deployment_id=\"{}\"}} {:.2}\n\
\n\
# HELP deployment_uptime_percentage Uptime percentage\n\
# TYPE deployment_uptime_percentage gauge\n\
deployment_uptime_percentage{{deployment_id=\"{}\"}} {:.2}\n\
\n\
# HELP deployment_total_requests Total requests processed\n\
# TYPE deployment_total_requests counter\n\
deployment_total_requests{{deployment_id=\"{}\"}} {}\n\
\n\
# HELP deployment_total_errors Total errors encountered\n\
# TYPE deployment_total_errors counter\n\
deployment_total_errors{{deployment_id=\"{}\"}} {}\n",
deployment_id, self.instance_count.load( Ordering::Relaxed ),
deployment_id, self.cpu_utilization(),
deployment_id, self.memory_utilization(),
deployment_id, self.request_rate(),
deployment_id, self.error_rate(),
deployment_id, self.response_time_ms(),
deployment_id, self.uptime_percentage(),
deployment_id, self.total_requests.load( Ordering::Relaxed ),
deployment_id, self.total_errors.load( Ordering::Relaxed )
)
}
}
impl Default for DeploymentMetrics
{
fn default() -> Self
{
Self::new()
}
}
#[ derive( Debug, Clone ) ]
pub struct PerformanceOptimizer
{
recommendations : Arc< Mutex< Vec< OptimizationRecommendation > > >,
analysis_history : Arc< Mutex< Vec< ( SystemTime, String ) > > >,
}
impl PerformanceOptimizer
{
pub fn new() -> Self
{
Self {
recommendations : Arc::new( Mutex::new( Vec::new() ) ),
analysis_history : Arc::new( Mutex::new( Vec::new() ) ),
}
}
pub fn analyze_deployment( &self, deployment : &super::orchestration::ModelDeployment ) -> Vec< OptimizationRecommendation >
{
let mut recommendations = Vec::new();
let metrics = deployment.get_metrics();
if metrics.cpu_utilization() > 90.0
{
recommendations.push( OptimizationRecommendation {
category : OptimizationCategory::ResourceAllocation,
priority : OptimizationPriority::High,
title : "High CPU Utilization Detected".to_string(),
description : format!(
"CPU utilization is at {:.1}%, consider scaling up or optimizing compute resources",
metrics.cpu_utilization()
),
estimated_impact : ImpactEstimate::High,
implementation_effort : ImplementationEffort::Medium,
} );
}
if metrics.memory_utilization() > 85.0
{
recommendations.push( OptimizationRecommendation {
category : OptimizationCategory::ResourceAllocation,
priority : OptimizationPriority::High,
title : "High Memory Utilization Detected".to_string(),
description : format!(
"Memory utilization is at {:.1}%, consider increasing memory allocation",
metrics.memory_utilization()
),
estimated_impact : ImpactEstimate::High,
implementation_effort : ImplementationEffort::Low,
} );
}
if metrics.response_time_ms() > 1000.0
{
recommendations.push( OptimizationRecommendation {
category : OptimizationCategory::Performance,
priority : OptimizationPriority::Medium,
title : "High Response Time Detected".to_string(),
description : format!(
"Average response time is {:.1}ms, consider optimizing model inference or adding caching",
metrics.response_time_ms()
),
estimated_impact : ImpactEstimate::Medium,
implementation_effort : ImplementationEffort::High,
} );
}
if metrics.error_rate() > 5.0
{
recommendations.push( OptimizationRecommendation {
category : OptimizationCategory::Reliability,
priority : OptimizationPriority::High,
title : "High Error Rate Detected".to_string(),
description : format!(
"Error rate is {:.1}%, investigate error patterns and improve error handling",
metrics.error_rate()
),
estimated_impact : ImpactEstimate::High,
implementation_effort : ImplementationEffort::Medium,
} );
}
{
let mut stored_recommendations = self.recommendations.lock().unwrap();
stored_recommendations.extend( recommendations.clone() );
let mut history = self.analysis_history.lock().unwrap();
history.push( (
SystemTime::now(),
format!( "Generated {} recommendations for deployment {}",
recommendations.len(), deployment.deployment_id )
) );
}
recommendations
}
pub fn get_recommendations( &self ) -> Vec< OptimizationRecommendation >
{
self.recommendations.lock().unwrap().clone()
}
pub fn clear_recommendations( &self )
{
self.recommendations.lock().unwrap().clear();
}
}
#[ derive( Debug, Clone ) ]
pub struct OptimizationRecommendation
{
pub category : OptimizationCategory,
pub priority : OptimizationPriority,
pub title : String,
pub description : String,
pub estimated_impact : ImpactEstimate,
pub implementation_effort : ImplementationEffort,
}
#[ derive( Debug, Clone, PartialEq, Eq ) ]
pub enum OptimizationCategory
{
ResourceAllocation,
Performance,
Reliability,
Cost,
Security,
}
#[ derive( Debug, Clone, PartialEq, Eq, PartialOrd, Ord ) ]
pub enum OptimizationPriority
{
Low,
Medium,
High,
Critical,
}
#[ derive( Debug, Clone, PartialEq, Eq ) ]
pub enum ImpactEstimate
{
Low,
Medium,
High,
}
#[ derive( Debug, Clone, PartialEq, Eq ) ]
pub enum ImplementationEffort
{
Low,
Medium,
High,
}