use serde::{Deserialize, Serialize};
use std::cmp::Reverse;
use std::collections::BTreeMap;
use std::collections::{BinaryHeap, HashMap};
use tokio::time::{TokioDuration, TokioInstant};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum TaskPriority {
Critical,
High,
Medium,
Low,
Background,
}
impl TaskPriority {
pub fn to_numeric(&self) -> u8 {
match self {
TaskPriority::Critical => 100,
TaskPriority::High => 75,
TaskPriority::Medium => 50,
TaskPriority::Low => 25,
TaskPriority::Background => 10,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Task {
pub id: String,
pub name: String,
pub priority: TaskPriority,
pub resource_requirements: HashMap<String, f64>,
pub estimated_duration_ms: u64,
pub created_at: Instant,
pub last_scheduled_at: Option<Instant>,
pub dependencies: Vec<String>,
pub tags: Vec<String>,
pub preemptible: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SchedulingDecision {
pub scheduled_task_id: String,
pub allocated_resources: HashMap<String, f64>,
pub estimated_start_time: Instant,
pub estimated_completion_time: Instant,
pub reason: String,
pub strategy: SchedulingStrategy,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum SchedulingStrategy {
PriorityFirst,
ShortestJobFirst,
EarliestDeadlineFirst,
ResourceEfficiencyFirst,
FairScheduling,
IntelligentAI,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ScheduleResult {
pub success: bool,
pub scheduled_tasks: Vec<SchedulingDecision>,
pub rejected_tasks: Vec<String>,
pub efficiency_score: f64,
pub resource_utilization: f64,
pub avg_wait_time_ms: u64,
pub message: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WorkloadProfile {
pub workload_type: String,
pub avg_duration_ms: u64,
pub avg_resource_usage: HashMap<String, f64>,
pub concurrency: usize,
pub priority_distribution: HashMap<TaskPriority, f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SchedulerState {
pub queued_tasks: usize,
pub running_tasks: usize,
pub completed_tasks: usize,
pub avg_scheduling_latency_ms: f64,
pub system_load: f64,
}
#[derive(Debug, Clone)]
pub struct Scheduler {
task_queue: BinaryHeap<Reverse<TaskWithPriority>>,
config: SchedulerConfig,
scheduling_history: Vec<SchedulingDecision>,
task_tracking: HashMap<String, TaskExecution>,
}
#[derive(Debug, Clone)]
struct TaskWithPriority {
task: Task,
insertion_order: usize,
}
impl PartialEq for TaskWithPriority {
fn eq(&self, other: &Self) -> bool {
self.task.id == other.task.id
}
}
impl Eq for TaskWithPriority {}
impl PartialOrd for TaskWithPriority {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TaskWithPriority {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let priority_cmp: _ = other
.task
.priority
.to_numeric()
.cmp(&self.task.priority.to_numeric());
if priority_cmp == std::cmp::Ordering::Equal {
self.task.created_at.cmp(&other.task.created_at)
} else {
priority_cmp
}
}
}
#[derive(Debug, Clone)]
struct TaskExecution {
task_id: String,
start_time: Instant,
allocated_resources: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct SchedulerConfig {
pub max_concurrent_tasks: usize,
pub scheduling_time_slice_ms: u64,
pub task_timeout_ms: u64,
pub enable_ai_scheduling: bool,
pub resource_reservation_percent: f64,
}
impl Default for SchedulerConfig {
fn default() -> Self {
Self {
max_concurrent_tasks: 100,
scheduling_time_slice_ms: 100,
task_timeout_ms: 300000, enable_ai_scheduling: true,
resource_reservation_percent: 10.0,
}
}
}
impl Scheduler {
pub fn new(config: SchedulerConfig) -> Self {
Self {
task_queue: BinaryHeap::new(),
config,
scheduling_history: Vec::new(),
task_tracking: HashMap::new(),
}
}
pub fn new_with_defaults() -> Self {
Self::new(SchedulerConfig::default())
}
pub async fn add_task(&mut self, task: Task) -> bool {
if !self.check_dependencies(&task) {
return false;
}
let task_with_priority: _ = TaskWithPriority {
task,
insertion_order: self.task_queue.len(),
};
self.task_queue.push(Reverse(task_with_priority));
true
}
pub async fn schedule_next(
&mut self,
available_resources: &HashMap<String, f64>,
strategy: SchedulingStrategy,
) -> ScheduleResult {
let mut scheduled_tasks = Vec::new();
let mut rejected_tasks = Vec::new();
let mut total_wait_time = 0u64;
let mut scheduled_count = 0;
let start_time: _ = Instant::now();
for _ in 0..self.config.max_concurrent_tasks {
if self.task_queue.is_empty() {
break;
}
if let Some(Reverse(task_with_priority)) = self.task_queue.peek() {
let task: _ = &task_with_priority.task;
if !self.check_dependencies(task) {
continue;
}
if self.has_sufficient_resources(task, available_resources) {
self.task_queue.pop();
let allocated_resources =
self.allocate_resources_for_task(task, available_resources);
let decision: _ = self.create_scheduling_decision(
task,
&allocated_resources,
strategy.clone(),
);
scheduled_tasks.push(decision.clone());
self.scheduling_history.push(decision);
scheduled_count += 1;
self.task_tracking.insert(
task.id.clone(),
TaskExecution {
task_id: task.id.clone(),
start_time: Instant::now(),
allocated_resources: allocated_resources.clone(),
},
);
let wait_time: _ =
Instant::now().duration_since(task.created_at).as_millis() as u64;
total_wait_time += wait_time;
} else {
rejected_tasks.push(task.id.clone());
}
} else {
break;
}
}
let efficiency_score: _ = self.calculate_efficiency_score(
scheduled_count,
rejected_tasks.len(),
available_resources,
);
let resource_utilization =
self.calculate_resource_utilization(available_resources, &scheduled_tasks);
let avg_wait_time_ms: _ = if scheduled_count > 0 {
total_wait_time / scheduled_count as u64
} else {
0
};
ScheduleResult {
success: scheduled_count > 0,
scheduled_tasks,
rejected_tasks,
efficiency_score,
resource_utilization,
avg_wait_time_ms,
message: format!(
"调度完成: {} 个任务已调度, {} 个任务被拒绝",
scheduled_count,
rejected_tasks.len()
),
}
}
pub async fn get_state(&self) -> SchedulerState {
let avg_latency: _ = if !self.scheduling_history.is_empty() {
let total_latency: u64 = self
.scheduling_history
.iter()
.map(|d| {
d.estimated_start_time
.duration_since(d.estimated_start_time)
.as_millis() as u64
})
.sum();
total_latency as f64 / self.scheduling_history.len() as f64
} else {
0.0
};
SchedulerState {
queued_tasks: self.task_queue.len(),
running_tasks: self.task_tracking.len(),
completed_tasks: 0, avg_scheduling_latency_ms: avg_latency,
system_load: self.calculate_system_load(),
}
}
pub async fn get_statistics(&self) -> SchedulerStatistics {
let mut priority_counts = HashMap::new();
for task in self.task_queue.iter() {
*priority_counts
.entry(task.task.priority.clone())
.or_insert(0) += 1;
}
SchedulerStatistics {
total_tasks_queued: self.task_queue.len(),
total_tasks_scheduled: self.scheduling_history.len(),
priority_distribution: priority_counts,
avg_efficiency_score: self.calculate_average_efficiency(),
total_resource_usage: self.calculate_total_resource_usage(),
}
}
fn check_dependencies(&self, task: &Task) -> bool {
for dep_id in &task.dependencies {
let in_queue: _ = self.task_queue.iter().any(|t| t.task.id == *dep_id);
let in_tracking: _ = self.task_tracking.contains_key(dep_id);
if in_queue || in_tracking {
return false;
}
}
true
}
fn has_sufficient_resources(
&self,
task: &Task,
available_resources: &HashMap<String, f64>,
) -> bool {
for (resource_type, required) in &task.resource_requirements {
let available: _ = available_resources
.get(resource_type)
.copied()
.unwrap_or(0.0);
if available < *required {
return false;
}
}
true
}
fn allocate_resources_for_task(
&self,
task: &Task,
available_resources: &HashMap<String, f64>,
) -> HashMap<String, f64> {
let mut allocated = HashMap::new();
for (resource_type, required) in &task.resource_requirements {
let available: _ = available_resources
.get(resource_type)
.copied()
.unwrap_or(0.0);
let reservation_factor: _ = 1.0 + (task.priority.to_numeric() as f64 / 100.0) * 0.1;
let reserved_required: _ = required * reservation_factor;
let to_allocate: _ = available.min(*required).min(reserved_required);
allocated.insert(resource_type.clone(), to_allocate);
}
allocated
}
fn create_scheduling_decision(
&self,
task: &Task,
allocated_resources: &HashMap<String, f64>,
strategy: SchedulingStrategy,
) -> SchedulingDecision {
let now: _ = Instant::now();
let estimated_completion: _ = now + Duration::from_millis(task.estimated_duration_ms);
let reason: _ = match strategy {
SchedulingStrategy::PriorityFirst => {
format!("使用优先级优先策略调度任务 (优先级: {:?})", task.priority)
}
SchedulingStrategy::ShortestJobFirst => {
format!(
"使用最短作业优先策略调度任务 (预计时间: {}ms)",
task.estimated_duration_ms
)
}
_ => "使用 AI 智能调度策略".to_string(),
};
SchedulingDecision {
scheduled_task_id: task.id.clone(),
allocated_resources: allocated_resources.clone(),
estimated_start_time: now,
estimated_completion_time: estimated_completion,
reason,
strategy,
}
}
fn calculate_efficiency_score(
&self,
scheduled_count: usize,
rejected_count: usize,
available_resources: &HashMap<String, f64>,
) -> f64 {
if scheduled_count == 0 && rejected_count == 0 {
return 100.0;
}
let total_tasks: _ = scheduled_count + rejected_count;
let success_rate: _ = scheduled_count as f64 / total_tasks as f64;
let resource_efficiency: _ = available_resources
.values()
.map(|&r| if r > 0.0 { 1.0 } else { 0.0 })
.sum::<f64>()
/ available_resources.len() as f64;
(success_rate * 70.0 + resource_efficiency * 30.0).min(100.0)
}
fn calculate_resource_utilization(
&self,
available_resources: &HashMap<String, f64>,
scheduled_tasks: &[SchedulingDecision],
) -> f64 {
let mut total_allocated = HashMap::new();
let mut total_available = 0.0;
for decision in scheduled_tasks {
for (resource_type, &amount) in &decision.allocated_resources {
*total_allocated.entry(resource_type.clone()).or_insert(0.0) += amount;
}
}
for &amount in available_resources.values() {
total_available += amount;
}
if total_available > 0.0 {
let total_allocated_sum: f64 = total_allocated.values().sum();
(total_allocated_sum / total_available) * 100.0
} else {
0.0
}
}
fn calculate_system_load(&self) -> f64 {
let queued_ratio: _ =
self.task_queue.len() as f64 / self.config.max_concurrent_tasks as f64;
let running_ratio: _ =
self.task_tracking.len() as f64 / self.config.max_concurrent_tasks as f64;
(queued_ratio + running_ratio) / 2.0 * 100.0
}
fn calculate_average_efficiency(&self) -> f64 {
if self.scheduling_history.is_empty() {
return 0.0;
}
75.0
}
fn calculate_total_resource_usage(&self) -> f64 {
self.task_tracking
.values()
.flat_map(|exec| exec.allocated_resources.values())
.sum()
}
pub async fn cancel_task(&mut self, task_id: &str) -> bool {
let task_to_remove: _ = self
.task_queue
.iter()
.find(|t| t.task.id == *task_id)
.cloned();
if let Some(Reverse(task)) = task_to_remove {
self.task_queue.retain(|t| t.task.id != *task_id);
true
} else {
false
}
}
pub async fn get_queued_tasks(&self) -> Vec<Task> {
self.task_queue.iter().map(|t| t.task.clone()).collect()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SchedulerStatistics {
pub total_tasks_queued: usize,
pub total_tasks_scheduled: usize,
pub priority_distribution: HashMap<TaskPriority, usize>,
pub avg_efficiency_score: f64,
pub total_resource_usage: f64,
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::Ordering;
use std::time::{Duration, Instant};
#[tokio::test]
async fn test_add_task() {
let mut scheduler = Scheduler::new_with_defaults();
let task: _ = Task {
id: "task-1".to_string(),
name: "Test Task".to_string(),
priority: TaskPriority::High,
resource_requirements: {
let mut map = HashMap::new();
map.insert("cpu".to_string(), 100.0);
map
},
estimated_duration_ms: 1000,
created_at: Instant::now(),
last_scheduled_at: None,
dependencies: vec![],
tags: vec![],
preemptible: true,
};
let result: _ = scheduler.add_task(task).await;
assert!(result);
}
#[tokio::test]
async fn test_schedule_next() {
let mut scheduler = Scheduler::new_with_defaults();
let task: _ = Task {
id: "task-1".to_string(),
name: "Test Task".to_string(),
priority: TaskPriority::High,
resource_requirements: {
let mut map = HashMap::new();
map.insert("cpu".to_string(), 50.0);
map
},
estimated_duration_ms: 1000,
created_at: Instant::now(),
last_scheduled_at: None,
dependencies: vec![],
tags: vec![],
preemptible: true,
};
scheduler.add_task(task).await;
let available_resources: _ = {
let mut map = HashMap::new();
map.insert("cpu".to_string(), 100.0);
map
};
let result: _ = scheduler
.schedule_next(&available_resources, SchedulingStrategy::PriorityFirst)
.await;
assert!(result.success);
assert!(!result.scheduled_tasks.is_empty());
}
#[tokio::test]
async fn test_task_priority_ordering() {
let high: _ = TaskPriority::High;
let low: _ = TaskPriority::Low;
assert!(high > low);
assert_eq!(high.to_numeric(), 75);
assert_eq!(low.to_numeric(), 25);
}
}