1use crate::controller::{Controller, ControllerSnapshot, Decision, Sample};
5
6#[derive(Debug, Clone, Copy)]
12pub struct FixedController {
13 decision: Decision,
14}
15
16impl FixedController {
17 pub fn new(decision: Decision) -> Self {
18 Self { decision }
19 }
20 pub fn with_concurrency(max_in_flight: u32) -> Self {
21 Self::new(Decision::with_concurrency(max_in_flight))
22 }
23 pub fn with_rate(rate_per_sec: f64) -> Self {
24 Self::new(Decision::with_rate(rate_per_sec))
25 }
26 pub fn with_concurrency_and_rate(max_in_flight: u32, rate_per_sec: f64) -> Self {
27 Self::new(Decision::with_concurrency_and_rate(
28 max_in_flight,
29 rate_per_sec,
30 ))
31 }
32}
33
34impl Controller for FixedController {
35 fn on_sample(&mut self, _sample: &Sample) {}
36 fn on_tick(&mut self, _now: std::time::Instant) -> Decision {
37 self.decision
38 }
39 fn name(&self) -> &'static str {
40 "fixed"
41 }
42 fn snapshot(&self) -> ControllerSnapshot {
43 ControllerSnapshot {
44 cwnd: self.decision.max_in_flight.unwrap_or(0),
45 ..ControllerSnapshot::default()
46 }
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use crate::controller::Outcome;
54
55 #[test]
56 fn emits_configured_concurrency_budget() {
57 let mut controller = FixedController::with_concurrency(16);
58 let decision = controller.on_tick(std::time::Instant::now());
59 assert_eq!(decision.max_in_flight, Some(16));
60 assert_eq!(decision.rate_per_sec, None);
61 }
62
63 #[test]
64 fn emits_configured_rate_budget() {
65 let mut controller = FixedController::with_rate(5000.0);
66 let decision = controller.on_tick(std::time::Instant::now());
67 assert_eq!(decision.max_in_flight, None);
68 assert_eq!(decision.rate_per_sec, Some(5000.0));
69 }
70
71 #[test]
72 fn samples_do_not_change_emitted_decision() {
73 let mut controller = FixedController::with_concurrency(4);
74 let start = std::time::Instant::now();
75 controller.on_sample(&Sample {
76 started_at: start,
77 completed_at: start + std::time::Duration::from_secs(60),
78 bytes: 0,
79 outcome: Outcome::Backpressure,
80 });
81 assert_eq!(controller.on_tick(start).max_in_flight, Some(4));
82 }
83}