1use std::time::Duration;
6
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub enum LoadPattern {
12 Steady {
14 ops_per_sec: u64,
16 },
17
18 Ramp {
20 start_ops: u64,
22 end_ops: u64,
24 duration: Duration,
26 },
27
28 Step {
30 initial: u64,
32 steps: Vec<(Duration, u64)>,
34 },
35
36 Wave {
38 baseline: u64,
40 amplitude: u64,
42 period: Duration,
44 },
45
46 Spike {
48 baseline: u64,
50 peak: u64,
52 spike_duration: Duration,
54 },
55
56 Burst {
58 baseline: u64,
60 burst_ops: u64,
62 burst_duration: Duration,
64 interval: Duration,
66 },
67
68 Random {
70 min_ops: u64,
72 max_ops: u64,
74 },
75
76 DailyTraffic {
78 peak: u64,
80 off_peak: u64,
82 day_duration: Duration,
84 },
85}
86
87impl Default for LoadPattern {
88 fn default() -> Self {
89 Self::Steady { ops_per_sec: 1000 }
90 }
91}
92
93impl LoadPattern {
94 pub fn ops_for_elapsed(&self, elapsed: Duration) -> u64 {
96 match self {
97 Self::Steady { ops_per_sec } => *ops_per_sec,
98
99 Self::Ramp {
100 start_ops,
101 end_ops,
102 duration,
103 } => {
104 let progress = (elapsed.as_secs_f64() / duration.as_secs_f64()).min(1.0);
105 let diff = *end_ops as f64 - *start_ops as f64;
106 (*start_ops as f64 + diff * progress) as u64
107 }
108
109 Self::Step { initial, steps } => {
110 let mut current = *initial;
111 for (time, level) in steps {
112 if elapsed >= *time {
113 current = *level;
114 }
115 }
116 current
117 }
118
119 Self::Wave {
120 baseline,
121 amplitude,
122 period,
123 } => {
124 let phase =
125 (elapsed.as_secs_f64() / period.as_secs_f64()) * 2.0 * std::f64::consts::PI;
126 let wave = phase.sin();
127 (*baseline as f64 + *amplitude as f64 * wave) as u64
128 }
129
130 Self::Spike {
131 baseline,
132 peak,
133 spike_duration,
134 } => {
135 if elapsed < *spike_duration {
138 *peak
139 } else {
140 *baseline
141 }
142 }
143
144 Self::Burst {
145 baseline,
146 burst_ops,
147 burst_duration,
148 interval,
149 } => {
150 let cycle = elapsed.as_millis() % interval.as_millis();
151 if cycle < burst_duration.as_millis() {
152 *burst_ops
153 } else {
154 *baseline
155 }
156 }
157
158 Self::Random { min_ops, max_ops } => {
159 let seed = elapsed.as_micros() as u64;
161 let range = *max_ops - *min_ops;
162 *min_ops + (seed % (range + 1))
163 }
164
165 Self::DailyTraffic {
166 peak,
167 off_peak,
168 day_duration,
169 } => {
170 let day_progress = (elapsed.as_secs_f64() % day_duration.as_secs_f64())
172 / day_duration.as_secs_f64();
173
174 let hour = day_progress * 24.0;
176 let traffic_factor = if hour >= 8.0 && hour <= 20.0 {
177 let peak_at_noon = 1.0 - ((hour - 14.0).abs() / 6.0);
179 0.5 + 0.5 * peak_at_noon
180 } else {
181 0.2
183 };
184
185 let range = *peak - *off_peak;
186 (*off_peak as f64 + range as f64 * traffic_factor) as u64
187 }
188 }
189 }
190
191 pub fn description(&self) -> String {
193 match self {
194 Self::Steady { ops_per_sec } => format!("Steady {} ops/s", ops_per_sec),
195 Self::Ramp {
196 start_ops,
197 end_ops,
198 duration,
199 } => format!(
200 "Ramp {} -> {} ops/s over {:?}",
201 start_ops, end_ops, duration
202 ),
203 Self::Step { initial, steps } => {
204 format!(
205 "Step pattern starting at {} with {} steps",
206 initial,
207 steps.len()
208 )
209 }
210 Self::Wave {
211 baseline,
212 amplitude,
213 period,
214 } => format!("Wave {}±{} ops/s, period {:?}", baseline, amplitude, period),
215 Self::Spike {
216 baseline,
217 peak,
218 spike_duration,
219 } => format!(
220 "Spike {} -> {} ops/s for {:?}",
221 baseline, peak, spike_duration
222 ),
223 Self::Burst {
224 baseline,
225 burst_ops,
226 burst_duration,
227 interval,
228 } => format!(
229 "Burst {}->{} ops/s for {:?} every {:?}",
230 baseline, burst_ops, burst_duration, interval
231 ),
232 Self::Random { min_ops, max_ops } => format!("Random {}-{} ops/s", min_ops, max_ops),
233 Self::DailyTraffic { peak, off_peak, .. } => {
234 format!("Daily traffic {}-{} ops/s", off_peak, peak)
235 }
236 }
237 }
238
239 pub fn steady(ops_per_sec: u64) -> Self {
241 Self::Steady { ops_per_sec }
242 }
243
244 pub fn ramp(start: u64, end: u64, duration: Duration) -> Self {
246 Self::Ramp {
247 start_ops: start,
248 end_ops: end,
249 duration,
250 }
251 }
252
253 pub fn wave(baseline: u64, amplitude: u64, period: Duration) -> Self {
255 Self::Wave {
256 baseline,
257 amplitude,
258 period,
259 }
260 }
261
262 pub fn spike(baseline: u64, peak: u64, spike_duration: Duration) -> Self {
264 Self::Spike {
265 baseline,
266 peak,
267 spike_duration,
268 }
269 }
270
271 pub fn burst(
273 baseline: u64,
274 burst_ops: u64,
275 burst_duration: Duration,
276 interval: Duration,
277 ) -> Self {
278 Self::Burst {
279 baseline,
280 burst_ops,
281 burst_duration,
282 interval,
283 }
284 }
285}
286
287#[derive(Default)]
289pub struct LoadPatternBuilder {
290 steps: Vec<(Duration, u64)>,
291 initial: u64,
292}
293
294impl LoadPatternBuilder {
295 pub fn new() -> Self {
297 Self::default()
298 }
299
300 pub fn initial(mut self, ops: u64) -> Self {
302 self.initial = ops;
303 self
304 }
305
306 pub fn step_at(mut self, time: Duration, ops: u64) -> Self {
308 self.steps.push((time, ops));
309 self
310 }
311
312 pub fn build_step(self) -> LoadPattern {
314 LoadPattern::Step {
315 initial: self.initial,
316 steps: self.steps,
317 }
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 #[test]
326 fn test_steady_pattern() {
327 let pattern = LoadPattern::steady(1000);
328
329 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 1000);
330 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(100)), 1000);
331 }
332
333 #[test]
334 fn test_ramp_pattern() {
335 let pattern = LoadPattern::ramp(100, 1000, Duration::from_secs(10));
336
337 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 100);
338 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(5)), 550);
339 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(10)), 1000);
340 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(20)), 1000); }
342
343 #[test]
344 fn test_step_pattern() {
345 let pattern = LoadPattern::Step {
346 initial: 100,
347 steps: vec![
348 (Duration::from_secs(5), 500),
349 (Duration::from_secs(10), 1000),
350 ],
351 };
352
353 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 100);
354 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(3)), 100);
355 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(5)), 500);
356 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(7)), 500);
357 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(10)), 1000);
358 }
359
360 #[test]
361 fn test_wave_pattern() {
362 let pattern = LoadPattern::wave(1000, 500, Duration::from_secs(10));
363
364 let at_0 = pattern.ops_for_elapsed(Duration::from_secs(0));
365 let at_quarter = pattern.ops_for_elapsed(Duration::from_millis(2500));
366 let at_half = pattern.ops_for_elapsed(Duration::from_secs(5));
367
368 assert_eq!(at_0, 1000);
370
371 assert!((at_quarter as i64 - 1500).abs() < 10);
373
374 assert!((at_half as i64 - 1000).abs() < 10);
376 }
377
378 #[test]
379 fn test_spike_pattern() {
380 let pattern = LoadPattern::spike(100, 10000, Duration::from_secs(5));
381
382 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 10000); assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(3)), 10000); assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(6)), 100); }
386
387 #[test]
388 fn test_burst_pattern() {
389 let pattern =
390 LoadPattern::burst(100, 5000, Duration::from_secs(2), Duration::from_secs(10));
391
392 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 5000);
394 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(1)), 5000);
395
396 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(3)), 100);
398 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(9)), 100);
399
400 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(10)), 5000);
402 }
403
404 #[test]
405 fn test_random_pattern() {
406 let pattern = LoadPattern::Random {
407 min_ops: 100,
408 max_ops: 1000,
409 };
410
411 for i in 0..10 {
412 let ops = pattern.ops_for_elapsed(Duration::from_secs(i));
413 assert!(ops >= 100 && ops <= 1000);
414 }
415 }
416
417 #[test]
418 fn test_daily_traffic_pattern() {
419 let pattern = LoadPattern::DailyTraffic {
420 peak: 10000,
421 off_peak: 1000,
422 day_duration: Duration::from_secs(86400), };
424
425 let midnight = pattern.ops_for_elapsed(Duration::from_secs(0));
427 let noon = pattern.ops_for_elapsed(Duration::from_secs(43200)); let evening = pattern.ops_for_elapsed(Duration::from_secs(72000)); assert!(midnight < 5000);
432
433 assert!(noon > 5000);
435
436 assert!(evening < noon);
438 }
439
440 #[test]
441 fn test_pattern_builder() {
442 let pattern = LoadPatternBuilder::new()
443 .initial(100)
444 .step_at(Duration::from_secs(5), 500)
445 .step_at(Duration::from_secs(10), 1000)
446 .build_step();
447
448 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 100);
449 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(5)), 500);
450 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(10)), 1000);
451 }
452
453 #[test]
454 fn test_pattern_descriptions() {
455 let patterns = vec![
456 LoadPattern::steady(1000),
457 LoadPattern::ramp(100, 1000, Duration::from_secs(10)),
458 LoadPattern::wave(500, 200, Duration::from_secs(30)),
459 LoadPattern::spike(100, 5000, Duration::from_secs(5)),
460 ];
461
462 for pattern in patterns {
463 let desc = pattern.description();
464 assert!(!desc.is_empty());
465 }
466 }
467}