use std::collections::HashMap;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct PredictiveSchedulerConfig {
pub target_slo_compliance: f64,
pub max_cost_per_op: f64,
pub enable_spot_instances: bool,
pub preemption_buffer: Duration,
pub load_decay_factor: f64,
pub min_capacity_threshold: f64,
pub slo_violation_penalty: f64,
pub history_window: usize,
}
impl Default for PredictiveSchedulerConfig {
fn default() -> Self {
Self {
target_slo_compliance: 0.99,
max_cost_per_op: 1.0,
enable_spot_instances: true,
preemption_buffer: Duration::from_secs(300), load_decay_factor: 0.9,
min_capacity_threshold: 0.8,
slo_violation_penalty: 10.0,
history_window: 100,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InstanceType {
OnDemand,
Spot,
Reserved,
Preemptible,
}
impl InstanceType {
pub fn cost_multiplier(&self) -> f64 {
match self {
Self::OnDemand => 1.0,
Self::Spot => 0.3, Self::Reserved => 0.6, Self::Preemptible => 0.2, }
}
pub fn reliability(&self) -> f64 {
match self {
Self::OnDemand => 0.9999,
Self::Reserved => 0.9999,
Self::Spot => 0.85,
Self::Preemptible => 0.90,
}
}
}
#[derive(Debug, Clone)]
pub struct HostProfile {
pub host_id: String,
pub instance_type: InstanceType,
pub compute_capacity: f64,
pub memory_capacity: u64,
pub current_load: f64,
pub hourly_cost: f64,
pub network_latency_ms: f64,
pub historical_slo_compliance: f64,
pub preemption_deadline: Option<Instant>,
pub performance_cv: f64,
}
impl HostProfile {
pub fn new(host_id: impl Into<String>, instance_type: InstanceType) -> Self {
Self {
host_id: host_id.into(),
instance_type,
compute_capacity: 1000.0,
memory_capacity: 8 * 1024 * 1024 * 1024, current_load: 0.0,
hourly_cost: 1.0,
network_latency_ms: 1.0,
historical_slo_compliance: 0.99,
preemption_deadline: None,
performance_cv: 0.1,
}
}
pub fn cost_per_op(&self) -> f64 {
let base_cost = self.hourly_cost * self.instance_type.cost_multiplier();
let effective_capacity = self.compute_capacity * (1.0 - self.current_load);
if effective_capacity > 0.0 {
base_cost / (effective_capacity * 3600.0)
} else {
f64::MAX
}
}
pub fn available_capacity(&self) -> f64 {
(1.0 - self.current_load).max(0.0)
}
pub fn time_until_preemption(&self) -> Option<Duration> {
self.preemption_deadline.map(|deadline| {
let now = Instant::now();
if deadline > now {
deadline - now
} else {
Duration::ZERO
}
})
}
pub fn is_safe_for_scheduling(&self, buffer: Duration) -> bool {
match self.time_until_preemption() {
Some(time_left) => time_left > buffer,
None => true, }
}
}
#[derive(Debug, Clone)]
pub struct WorkloadSpec {
pub workload_id: String,
pub operation_count: u64,
pub memory_required: u64,
pub slo_deadline: Duration,
pub priority: u32,
pub preemptible: bool,
pub compute_intensity: f64,
}
impl WorkloadSpec {
pub fn new(workload_id: impl Into<String>, operation_count: u64) -> Self {
Self {
workload_id: workload_id.into(),
operation_count,
memory_required: 1024 * 1024, slo_deadline: Duration::from_millis(100),
priority: 1,
preemptible: true,
compute_intensity: 100.0,
}
}
pub fn estimated_execution_time(&self, host: &HostProfile) -> Duration {
let ops_per_sec = host.compute_capacity * host.available_capacity();
if ops_per_sec > 0.0 {
let seconds = self.operation_count as f64 / ops_per_sec;
Duration::from_secs_f64(seconds)
} else {
Duration::MAX
}
}
}
#[derive(Debug, Clone)]
pub struct SchedulingDecision {
pub host_id: String,
pub predicted_time: Duration,
pub predicted_cost: f64,
pub slo_compliance_prob: f64,
pub score: f64,
pub reason: String,
}
#[derive(Debug, Clone, Default)]
pub struct SchedulerMetrics {
pub total_decisions: u64,
pub slo_violations: u64,
pub total_cost: f64,
pub avg_scheduling_latency_us: f64,
pub host_utilization: HashMap<String, f64>,
pub spot_savings: f64,
}
impl SchedulerMetrics {
pub fn slo_compliance_rate(&self) -> f64 {
if self.total_decisions > 0 {
1.0 - (self.slo_violations as f64 / self.total_decisions as f64)
} else {
1.0
}
}
pub fn avg_cost_per_decision(&self) -> f64 {
if self.total_decisions > 0 {
self.total_cost / self.total_decisions as f64
} else {
0.0
}
}
}