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 = (elapsed.as_secs_f64() / period.as_secs_f64()) * 2.0 * std::f64::consts::PI;
125 let wave = phase.sin();
126 (*baseline as f64 + *amplitude as f64 * wave) as u64
127 }
128
129 Self::Spike {
130 baseline,
131 peak,
132 spike_duration,
133 } => {
134 if elapsed < *spike_duration {
137 *peak
138 } else {
139 *baseline
140 }
141 }
142
143 Self::Burst {
144 baseline,
145 burst_ops,
146 burst_duration,
147 interval,
148 } => {
149 let cycle = elapsed.as_millis() % interval.as_millis();
150 if cycle < burst_duration.as_millis() {
151 *burst_ops
152 } else {
153 *baseline
154 }
155 }
156
157 Self::Random { min_ops, max_ops } => {
158 let seed = elapsed.as_micros() as u64;
160 let range = *max_ops - *min_ops;
161 *min_ops + (seed % (range + 1))
162 }
163
164 Self::DailyTraffic {
165 peak,
166 off_peak,
167 day_duration,
168 } => {
169 let day_progress = (elapsed.as_secs_f64() % day_duration.as_secs_f64())
171 / day_duration.as_secs_f64();
172
173 let hour = day_progress * 24.0;
175 let traffic_factor = if hour >= 8.0 && hour <= 20.0 {
176 let peak_at_noon = 1.0 - ((hour - 14.0).abs() / 6.0);
178 0.5 + 0.5 * peak_at_noon
179 } else {
180 0.2
182 };
183
184 let range = *peak - *off_peak;
185 (*off_peak as f64 + range as f64 * traffic_factor) as u64
186 }
187 }
188 }
189
190 pub fn description(&self) -> String {
192 match self {
193 Self::Steady { ops_per_sec } => format!("Steady {} ops/s", ops_per_sec),
194 Self::Ramp {
195 start_ops,
196 end_ops,
197 duration,
198 } => format!(
199 "Ramp {} -> {} ops/s over {:?}",
200 start_ops, end_ops, duration
201 ),
202 Self::Step { initial, steps } => {
203 format!("Step pattern starting at {} with {} steps", initial, steps.len())
204 }
205 Self::Wave {
206 baseline,
207 amplitude,
208 period,
209 } => format!(
210 "Wave {}±{} ops/s, period {:?}",
211 baseline, amplitude, period
212 ),
213 Self::Spike {
214 baseline,
215 peak,
216 spike_duration,
217 } => format!(
218 "Spike {} -> {} ops/s for {:?}",
219 baseline, peak, spike_duration
220 ),
221 Self::Burst {
222 baseline,
223 burst_ops,
224 burst_duration,
225 interval,
226 } => format!(
227 "Burst {}->{} ops/s for {:?} every {:?}",
228 baseline, burst_ops, burst_duration, interval
229 ),
230 Self::Random { min_ops, max_ops } => format!("Random {}-{} ops/s", min_ops, max_ops),
231 Self::DailyTraffic { peak, off_peak, .. } => {
232 format!("Daily traffic {}-{} ops/s", off_peak, peak)
233 }
234 }
235 }
236
237 pub fn steady(ops_per_sec: u64) -> Self {
239 Self::Steady { ops_per_sec }
240 }
241
242 pub fn ramp(start: u64, end: u64, duration: Duration) -> Self {
244 Self::Ramp {
245 start_ops: start,
246 end_ops: end,
247 duration,
248 }
249 }
250
251 pub fn wave(baseline: u64, amplitude: u64, period: Duration) -> Self {
253 Self::Wave {
254 baseline,
255 amplitude,
256 period,
257 }
258 }
259
260 pub fn spike(baseline: u64, peak: u64, spike_duration: Duration) -> Self {
262 Self::Spike {
263 baseline,
264 peak,
265 spike_duration,
266 }
267 }
268
269 pub fn burst(
271 baseline: u64,
272 burst_ops: u64,
273 burst_duration: Duration,
274 interval: Duration,
275 ) -> Self {
276 Self::Burst {
277 baseline,
278 burst_ops,
279 burst_duration,
280 interval,
281 }
282 }
283}
284
285#[derive(Default)]
287pub struct LoadPatternBuilder {
288 steps: Vec<(Duration, u64)>,
289 initial: u64,
290}
291
292impl LoadPatternBuilder {
293 pub fn new() -> Self {
295 Self::default()
296 }
297
298 pub fn initial(mut self, ops: u64) -> Self {
300 self.initial = ops;
301 self
302 }
303
304 pub fn step_at(mut self, time: Duration, ops: u64) -> Self {
306 self.steps.push((time, ops));
307 self
308 }
309
310 pub fn build_step(self) -> LoadPattern {
312 LoadPattern::Step {
313 initial: self.initial,
314 steps: self.steps,
315 }
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322
323 #[test]
324 fn test_steady_pattern() {
325 let pattern = LoadPattern::steady(1000);
326
327 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 1000);
328 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(100)), 1000);
329 }
330
331 #[test]
332 fn test_ramp_pattern() {
333 let pattern = LoadPattern::ramp(100, 1000, Duration::from_secs(10));
334
335 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 100);
336 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(5)), 550);
337 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(10)), 1000);
338 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(20)), 1000); }
340
341 #[test]
342 fn test_step_pattern() {
343 let pattern = LoadPattern::Step {
344 initial: 100,
345 steps: vec![
346 (Duration::from_secs(5), 500),
347 (Duration::from_secs(10), 1000),
348 ],
349 };
350
351 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 100);
352 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(3)), 100);
353 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(5)), 500);
354 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(7)), 500);
355 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(10)), 1000);
356 }
357
358 #[test]
359 fn test_wave_pattern() {
360 let pattern = LoadPattern::wave(1000, 500, Duration::from_secs(10));
361
362 let at_0 = pattern.ops_for_elapsed(Duration::from_secs(0));
363 let at_quarter = pattern.ops_for_elapsed(Duration::from_millis(2500));
364 let at_half = pattern.ops_for_elapsed(Duration::from_secs(5));
365
366 assert_eq!(at_0, 1000);
368
369 assert!((at_quarter as i64 - 1500).abs() < 10);
371
372 assert!((at_half as i64 - 1000).abs() < 10);
374 }
375
376 #[test]
377 fn test_spike_pattern() {
378 let pattern = LoadPattern::spike(100, 10000, Duration::from_secs(5));
379
380 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); }
384
385 #[test]
386 fn test_burst_pattern() {
387 let pattern = LoadPattern::burst(
388 100,
389 5000,
390 Duration::from_secs(2),
391 Duration::from_secs(10),
392 );
393
394 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 5000);
396 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(1)), 5000);
397
398 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(3)), 100);
400 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(9)), 100);
401
402 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(10)), 5000);
404 }
405
406 #[test]
407 fn test_random_pattern() {
408 let pattern = LoadPattern::Random {
409 min_ops: 100,
410 max_ops: 1000,
411 };
412
413 for i in 0..10 {
414 let ops = pattern.ops_for_elapsed(Duration::from_secs(i));
415 assert!(ops >= 100 && ops <= 1000);
416 }
417 }
418
419 #[test]
420 fn test_daily_traffic_pattern() {
421 let pattern = LoadPattern::DailyTraffic {
422 peak: 10000,
423 off_peak: 1000,
424 day_duration: Duration::from_secs(86400), };
426
427 let midnight = pattern.ops_for_elapsed(Duration::from_secs(0));
429 let noon = pattern.ops_for_elapsed(Duration::from_secs(43200)); let evening = pattern.ops_for_elapsed(Duration::from_secs(72000)); assert!(midnight < 5000);
434
435 assert!(noon > 5000);
437
438 assert!(evening < noon);
440 }
441
442 #[test]
443 fn test_pattern_builder() {
444 let pattern = LoadPatternBuilder::new()
445 .initial(100)
446 .step_at(Duration::from_secs(5), 500)
447 .step_at(Duration::from_secs(10), 1000)
448 .build_step();
449
450 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(0)), 100);
451 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(5)), 500);
452 assert_eq!(pattern.ops_for_elapsed(Duration::from_secs(10)), 1000);
453 }
454
455 #[test]
456 fn test_pattern_descriptions() {
457 let patterns = vec![
458 LoadPattern::steady(1000),
459 LoadPattern::ramp(100, 1000, Duration::from_secs(10)),
460 LoadPattern::wave(500, 200, Duration::from_secs(30)),
461 LoadPattern::spike(100, 5000, Duration::from_secs(5)),
462 ];
463
464 for pattern in patterns {
465 let desc = pattern.description();
466 assert!(!desc.is_empty());
467 }
468 }
469}