celers_beat/wfq.rs
1//! Weighted Fair Queuing (WFQ) types
2//!
3//! Provides WFQ configuration, task weights, and state management for
4//! fair scheduling based on task importance.
5
6use chrono::{DateTime, Utc};
7
8// ============================================================================
9// Weighted Fair Queuing (WFQ)
10// ============================================================================
11
12/// Weighted Fair Queuing configuration
13///
14/// WFQ provides fair scheduling based on task weights, ensuring that higher-weight
15/// tasks get proportionally more execution time while preventing starvation of
16/// lower-weight tasks.
17///
18/// # Algorithm
19/// - Each task has a weight (default: 1.0, range: 0.1-10.0)
20/// - Virtual time tracks fairness: virtual_finish_time = virtual_start_time + (execution_cost / weight)
21/// - Tasks with lowest virtual finish time are scheduled first
22/// - Prevents starvation while respecting priorities
23#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
24pub struct WFQConfig {
25 /// Enable Weighted Fair Queuing
26 pub enabled: bool,
27
28 /// Default weight for tasks without explicit weight
29 pub default_weight: f64,
30
31 /// Minimum allowed weight
32 pub min_weight: f64,
33
34 /// Maximum allowed weight
35 pub max_weight: f64,
36}
37
38impl Default for WFQConfig {
39 fn default() -> Self {
40 Self {
41 enabled: false,
42 default_weight: 1.0,
43 min_weight: 0.1,
44 max_weight: 10.0,
45 }
46 }
47}
48
49/// Task weight for Weighted Fair Queuing
50///
51/// Higher weights mean higher importance and more execution time allocation.
52/// Weight must be between 0.1 and 10.0.
53#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
54pub struct TaskWeight(f64);
55
56impl TaskWeight {
57 /// Create new task weight with validation
58 pub fn new(weight: f64) -> Result<Self, String> {
59 if !(0.1..=10.0).contains(&weight) {
60 return Err(format!(
61 "Weight must be between 0.1 and 10.0, got {}",
62 weight
63 ));
64 }
65 Ok(Self(weight))
66 }
67
68 /// Get the weight value
69 pub fn value(&self) -> f64 {
70 self.0
71 }
72}
73
74impl Default for TaskWeight {
75 fn default() -> Self {
76 Self(1.0)
77 }
78}
79
80/// Weighted Fair Queuing state for a task
81#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
82pub struct WFQState {
83 /// Task weight (higher = more important)
84 #[serde(default)]
85 pub weight: TaskWeight,
86
87 /// Virtual start time
88 #[serde(default)]
89 pub virtual_start_time: f64,
90
91 /// Virtual finish time (determines scheduling order)
92 #[serde(default)]
93 pub virtual_finish_time: f64,
94
95 /// Total execution time consumed (seconds)
96 #[serde(default)]
97 pub total_execution_time: f64,
98}
99
100impl Default for WFQState {
101 fn default() -> Self {
102 Self {
103 weight: TaskWeight::default(),
104 virtual_start_time: 0.0,
105 virtual_finish_time: 0.0,
106 total_execution_time: 0.0,
107 }
108 }
109}
110
111impl WFQState {
112 /// Create new WFQ state with specified weight
113 pub fn with_weight(weight: f64) -> Result<Self, String> {
114 Ok(Self {
115 weight: TaskWeight::new(weight)?,
116 ..Default::default()
117 })
118 }
119
120 /// Update virtual time after task execution
121 ///
122 /// # Arguments
123 /// * `execution_duration_secs` - Actual execution time in seconds
124 /// * `global_virtual_time` - Current global virtual time
125 pub fn update_after_execution(
126 &mut self,
127 execution_duration_secs: f64,
128 global_virtual_time: f64,
129 ) {
130 self.total_execution_time += execution_duration_secs;
131 self.virtual_start_time = global_virtual_time.max(self.virtual_finish_time);
132
133 // Virtual finish time = virtual start time + (cost / weight)
134 // Cost is the execution duration
135 self.virtual_finish_time =
136 self.virtual_start_time + (execution_duration_secs / self.weight.value());
137 }
138
139 /// Get the virtual finish time for scheduling decisions
140 pub fn finish_time(&self) -> f64 {
141 self.virtual_finish_time
142 }
143}
144
145/// Task information for WFQ scheduling
146#[derive(Debug, Clone)]
147pub struct WFQTaskInfo {
148 /// Task name
149 pub name: String,
150
151 /// Virtual finish time
152 pub virtual_finish_time: f64,
153
154 /// Task weight
155 pub weight: f64,
156
157 /// Next scheduled run time
158 pub next_run_time: DateTime<Utc>,
159}