rusmes_loadtest/
workload.rs1use std::time::{Duration, Instant};
4
5#[derive(Debug, Clone)]
7pub enum WorkloadPattern {
8 Steady { rate: u64 },
10
11 Spike {
13 baseline: u64,
14 peak: u64,
15 spike_duration: Duration,
16 spike_start: Duration,
17 },
18
19 RampUp {
21 start_rate: u64,
22 end_rate: u64,
23 duration: Duration,
24 },
25
26 Stress {
28 start_rate: u64,
29 increment: u64,
30 interval: Duration,
31 },
32
33 Wave {
35 min_rate: u64,
36 max_rate: u64,
37 period: Duration,
38 },
39}
40
41impl WorkloadPattern {
42 pub fn rate_at(&self, elapsed: Duration) -> u64 {
44 match self {
45 WorkloadPattern::Steady { rate } => *rate,
46
47 WorkloadPattern::Spike {
48 baseline,
49 peak,
50 spike_duration,
51 spike_start,
52 } => {
53 if elapsed >= *spike_start && elapsed < *spike_start + *spike_duration {
54 *peak
55 } else {
56 *baseline
57 }
58 }
59
60 WorkloadPattern::RampUp {
61 start_rate,
62 end_rate,
63 duration,
64 } => {
65 if elapsed >= *duration {
66 *end_rate
67 } else {
68 let progress = elapsed.as_secs_f64() / duration.as_secs_f64();
69 let rate_diff = *end_rate as f64 - *start_rate as f64;
70 (*start_rate as f64 + rate_diff * progress) as u64
71 }
72 }
73
74 WorkloadPattern::Stress {
75 start_rate,
76 increment,
77 interval,
78 } => {
79 let intervals = elapsed.as_secs() / interval.as_secs();
80 *start_rate + (*increment * intervals)
81 }
82
83 WorkloadPattern::Wave {
84 min_rate,
85 max_rate,
86 period,
87 } => {
88 let progress =
89 (elapsed.as_secs_f64() % period.as_secs_f64()) / period.as_secs_f64();
90 let amplitude = (*max_rate - *min_rate) as f64 / 2.0;
91 let center = (*min_rate + *max_rate) as f64 / 2.0;
92 let rate = center + amplitude * (progress * 2.0 * std::f64::consts::PI).sin();
93 rate as u64
94 }
95 }
96 }
97
98 pub fn delay_for_rate(rate: u64) -> Duration {
100 if rate == 0 {
101 Duration::from_secs(1)
102 } else {
103 Duration::from_micros(1_000_000 / rate)
104 }
105 }
106}
107
108pub struct WorkloadController {
110 pattern: WorkloadPattern,
111 start_time: Instant,
112}
113
114impl WorkloadController {
115 pub fn new(pattern: WorkloadPattern) -> Self {
117 Self {
118 pattern,
119 start_time: Instant::now(),
120 }
121 }
122
123 pub fn current_rate(&self) -> u64 {
125 let elapsed = self.start_time.elapsed();
126 self.pattern.rate_at(elapsed)
127 }
128
129 pub fn next_delay(&self) -> Duration {
131 let rate = self.current_rate();
132 WorkloadPattern::delay_for_rate(rate)
133 }
134
135 pub fn reset(&mut self) {
137 self.start_time = Instant::now();
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_steady_workload() {
147 let pattern = WorkloadPattern::Steady { rate: 100 };
148 assert_eq!(pattern.rate_at(Duration::from_secs(0)), 100);
149 assert_eq!(pattern.rate_at(Duration::from_secs(10)), 100);
150 assert_eq!(pattern.rate_at(Duration::from_secs(100)), 100);
151 }
152
153 #[test]
154 fn test_spike_workload() {
155 let pattern = WorkloadPattern::Spike {
156 baseline: 100,
157 peak: 1000,
158 spike_duration: Duration::from_secs(10),
159 spike_start: Duration::from_secs(5),
160 };
161
162 assert_eq!(pattern.rate_at(Duration::from_secs(0)), 100);
163 assert_eq!(pattern.rate_at(Duration::from_secs(7)), 1000);
164 assert_eq!(pattern.rate_at(Duration::from_secs(20)), 100);
165 }
166
167 #[test]
168 fn test_rampup_workload() {
169 let pattern = WorkloadPattern::RampUp {
170 start_rate: 100,
171 end_rate: 1000,
172 duration: Duration::from_secs(10),
173 };
174
175 assert_eq!(pattern.rate_at(Duration::from_secs(0)), 100);
176 assert_eq!(pattern.rate_at(Duration::from_secs(5)), 550);
177 assert_eq!(pattern.rate_at(Duration::from_secs(10)), 1000);
178 assert_eq!(pattern.rate_at(Duration::from_secs(20)), 1000);
179 }
180
181 #[test]
182 fn test_stress_workload() {
183 let pattern = WorkloadPattern::Stress {
184 start_rate: 100,
185 increment: 50,
186 interval: Duration::from_secs(10),
187 };
188
189 assert_eq!(pattern.rate_at(Duration::from_secs(0)), 100);
190 assert_eq!(pattern.rate_at(Duration::from_secs(10)), 150);
191 assert_eq!(pattern.rate_at(Duration::from_secs(20)), 200);
192 assert_eq!(pattern.rate_at(Duration::from_secs(30)), 250);
193 }
194
195 #[test]
196 fn test_wave_workload() {
197 let pattern = WorkloadPattern::Wave {
198 min_rate: 100,
199 max_rate: 500,
200 period: Duration::from_secs(60),
201 };
202
203 let rate_0 = pattern.rate_at(Duration::from_secs(0));
204 let rate_15 = pattern.rate_at(Duration::from_secs(15));
205 let rate_30 = pattern.rate_at(Duration::from_secs(30));
206
207 assert!((rate_0 as i64 - 300).abs() < 10);
209 assert!(rate_15 > 400);
211 assert!((rate_30 as i64 - 300).abs() < 10);
213 }
214
215 #[test]
216 fn test_delay_calculation() {
217 let delay_100 = WorkloadPattern::delay_for_rate(100);
218 assert_eq!(delay_100, Duration::from_micros(10_000));
219
220 let delay_1000 = WorkloadPattern::delay_for_rate(1000);
221 assert_eq!(delay_1000, Duration::from_micros(1_000));
222 }
223
224 #[test]
225 fn test_workload_controller() {
226 let pattern = WorkloadPattern::Steady { rate: 100 };
227 let controller = WorkloadController::new(pattern);
228
229 assert_eq!(controller.current_rate(), 100);
230 assert_eq!(controller.next_delay(), Duration::from_micros(10_000));
231 }
232
233 #[test]
234 fn test_workload_controller_reset() {
235 let pattern = WorkloadPattern::RampUp {
236 start_rate: 100,
237 end_rate: 1000,
238 duration: Duration::from_secs(10),
239 };
240 let mut controller = WorkloadController::new(pattern);
241
242 std::thread::sleep(Duration::from_millis(100));
243 let rate_before = controller.current_rate();
244
245 controller.reset();
246 let rate_after = controller.current_rate();
247
248 assert!(rate_after < rate_before || rate_before == 100);
250 }
251}