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 match 1_000_000u64.checked_div(rate) {
101 Some(micros) => Duration::from_micros(micros),
102 None => Duration::from_secs(1),
103 }
104 }
105}
106
107pub struct WorkloadController {
109 pattern: WorkloadPattern,
110 start_time: Instant,
111}
112
113impl WorkloadController {
114 pub fn new(pattern: WorkloadPattern) -> Self {
116 Self {
117 pattern,
118 start_time: Instant::now(),
119 }
120 }
121
122 pub fn current_rate(&self) -> u64 {
124 let elapsed = self.start_time.elapsed();
125 self.pattern.rate_at(elapsed)
126 }
127
128 pub fn next_delay(&self) -> Duration {
130 let rate = self.current_rate();
131 WorkloadPattern::delay_for_rate(rate)
132 }
133
134 pub fn reset(&mut self) {
136 self.start_time = Instant::now();
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn test_steady_workload() {
146 let pattern = WorkloadPattern::Steady { rate: 100 };
147 assert_eq!(pattern.rate_at(Duration::from_secs(0)), 100);
148 assert_eq!(pattern.rate_at(Duration::from_secs(10)), 100);
149 assert_eq!(pattern.rate_at(Duration::from_secs(100)), 100);
150 }
151
152 #[test]
153 fn test_spike_workload() {
154 let pattern = WorkloadPattern::Spike {
155 baseline: 100,
156 peak: 1000,
157 spike_duration: Duration::from_secs(10),
158 spike_start: Duration::from_secs(5),
159 };
160
161 assert_eq!(pattern.rate_at(Duration::from_secs(0)), 100);
162 assert_eq!(pattern.rate_at(Duration::from_secs(7)), 1000);
163 assert_eq!(pattern.rate_at(Duration::from_secs(20)), 100);
164 }
165
166 #[test]
167 fn test_rampup_workload() {
168 let pattern = WorkloadPattern::RampUp {
169 start_rate: 100,
170 end_rate: 1000,
171 duration: Duration::from_secs(10),
172 };
173
174 assert_eq!(pattern.rate_at(Duration::from_secs(0)), 100);
175 assert_eq!(pattern.rate_at(Duration::from_secs(5)), 550);
176 assert_eq!(pattern.rate_at(Duration::from_secs(10)), 1000);
177 assert_eq!(pattern.rate_at(Duration::from_secs(20)), 1000);
178 }
179
180 #[test]
181 fn test_stress_workload() {
182 let pattern = WorkloadPattern::Stress {
183 start_rate: 100,
184 increment: 50,
185 interval: Duration::from_secs(10),
186 };
187
188 assert_eq!(pattern.rate_at(Duration::from_secs(0)), 100);
189 assert_eq!(pattern.rate_at(Duration::from_secs(10)), 150);
190 assert_eq!(pattern.rate_at(Duration::from_secs(20)), 200);
191 assert_eq!(pattern.rate_at(Duration::from_secs(30)), 250);
192 }
193
194 #[test]
195 fn test_wave_workload() {
196 let pattern = WorkloadPattern::Wave {
197 min_rate: 100,
198 max_rate: 500,
199 period: Duration::from_secs(60),
200 };
201
202 let rate_0 = pattern.rate_at(Duration::from_secs(0));
203 let rate_15 = pattern.rate_at(Duration::from_secs(15));
204 let rate_30 = pattern.rate_at(Duration::from_secs(30));
205
206 assert!((rate_0 as i64 - 300).abs() < 10);
208 assert!(rate_15 > 400);
210 assert!((rate_30 as i64 - 300).abs() < 10);
212 }
213
214 #[test]
215 fn test_delay_calculation() {
216 let delay_100 = WorkloadPattern::delay_for_rate(100);
217 assert_eq!(delay_100, Duration::from_micros(10_000));
218
219 let delay_1000 = WorkloadPattern::delay_for_rate(1000);
220 assert_eq!(delay_1000, Duration::from_micros(1_000));
221 }
222
223 #[test]
224 fn test_workload_controller() {
225 let pattern = WorkloadPattern::Steady { rate: 100 };
226 let controller = WorkloadController::new(pattern);
227
228 assert_eq!(controller.current_rate(), 100);
229 assert_eq!(controller.next_delay(), Duration::from_micros(10_000));
230 }
231
232 #[test]
233 fn test_workload_controller_reset() {
234 let pattern = WorkloadPattern::RampUp {
235 start_rate: 100,
236 end_rate: 1000,
237 duration: Duration::from_secs(10),
238 };
239 let mut controller = WorkloadController::new(pattern);
240
241 std::thread::sleep(Duration::from_millis(100));
242 let rate_before = controller.current_rate();
243
244 controller.reset();
245 let rate_after = controller.current_rate();
246
247 assert!(rate_after < rate_before || rate_before == 100);
249 }
250}