noq_proto/connection/
pacing.rs

1//! Pacing of packet transmissions.
2
3use crate::congestion::ControllerMetrics;
4use crate::{Duration, Instant};
5
6use tracing::warn;
7
8/// A simple token-bucket pacer
9///
10/// The pacer's capacity is derived on a fraction of the congestion window
11/// which can be sent in regular intervals
12/// Once the bucket is empty, further transmission is blocked.
13/// The bucket refills at a rate slightly faster
14/// than one congestion window per RTT, as recommended in
15/// <https://tools.ietf.org/html/draft-ietf-quic-recovery-34#section-7.7>
16#[derive(Debug)]
17pub(super) struct Pacer {
18    capacity: u64,
19    /// Inputs [`Self::capacity`] was derived from, or `None` if it was derived from a pacing rate.
20    last_window_inputs: Option<WindowInputs>,
21    tokens: u64,
22    max_bytes_per_second: Option<u64>,
23    prev: Instant,
24}
25
26/// Inputs a window-derived [`Pacer::capacity`] was calculated from.
27#[derive(Copy, Clone, Eq, PartialEq, Debug)]
28struct WindowInputs {
29    /// Congestion window in bytes, after [`rate_limited_window`] has clamped it.
30    window: u64,
31    /// MTU of the path in bytes.
32    mtu: u16,
33}
34
35impl Pacer {
36    /// Obtains a new [`Pacer`].
37    pub(super) fn new(
38        smoothed_rtt: Duration,
39        window: u64,
40        mtu: u16,
41        max_bytes_per_second: Option<u64>,
42        now: Instant,
43    ) -> Self {
44        let window = rate_limited_window(smoothed_rtt, window, max_bytes_per_second);
45        let capacity = optimal_capacity(smoothed_rtt, window, mtu);
46        Self {
47            capacity,
48            last_window_inputs: Some(WindowInputs { window, mtu }),
49            tokens: capacity,
50            max_bytes_per_second,
51            prev: now,
52        }
53    }
54
55    /// Obtains the `max_bytes_per_second` used when this [`Pacer`] was constructed.
56    pub(crate) fn max_bytes_per_second(&self) -> Option<u64> {
57        self.max_bytes_per_second
58    }
59
60    /// Record that a packet has been transmitted.
61    pub(super) fn on_transmit(&mut self, packet_length: u16) {
62        self.tokens = self.tokens.saturating_sub(packet_length.into())
63    }
64
65    /// Return how long we need to wait before sending `bytes_to_send`.
66    ///
67    /// If we can send a packet right away, this returns `None`. Otherwise, returns `Some(d)`, where
68    /// `d` is the time before this function should be called again.
69    ///
70    /// The 5/4 ratio used here comes from the suggestion that N = 1.25 in the draft IETF RFC for
71    /// QUIC.
72    /// `controller_metrics` provides [`ControllerMetrics`] from the congestion controller used to
73    /// adjust pacing.
74    ///
75    /// Two of its fields are consumed here:
76    /// - `congestion_window` (bytes) sets the refill rate when the controller does not compute a
77    ///   rate of its own: one window per `smoothed_rtt`, times the 5/4 ratio above.
78    /// - `pacing_rate` (bytes/sec) sets the upper limit of how fast we're sending data, and takes
79    ///   precedence over `congestion_window` when present. e.g:
80    ///   <https://www.ietf.org/archive/id/draft-ietf-ccwg-bbr-04.html#name-pacing-rate-cpacing_rate>
81    pub(super) fn delay(
82        &mut self,
83        smoothed_rtt: Duration,
84        bytes_to_send: u64,
85        mtu: u16,
86        now: Instant,
87        controller_metrics: &ControllerMetrics,
88    ) -> Option<Duration> {
89        let window = controller_metrics.congestion_window;
90        debug_assert_ne!(
91            window, 0,
92            "zero-sized congestion control window is nonsense"
93        );
94
95        // A controller that computes its own sending rate drives the bucket directly; the
96        // window- and RTT-derived refill below is used only when no rate is reported.
97        if let Some(pacing_rate) = controller_metrics.pacing_rate {
98            return self.delay_at_rate(pacing_rate, bytes_to_send, mtu, now);
99        }
100
101        let window = rate_limited_window(smoothed_rtt, window, self.max_bytes_per_second);
102        let inputs = WindowInputs { window, mtu };
103        if self.last_window_inputs != Some(inputs) {
104            self.capacity = optimal_capacity(smoothed_rtt, window, mtu);
105
106            // here we cap the number of bytes sent at once during a burst
107            self.tokens = self.capacity.min(self.tokens);
108            self.last_window_inputs = Some(inputs);
109        }
110
111        // if we can already send a packet, there is no need for delay
112        if self.tokens >= bytes_to_send {
113            return None;
114        }
115
116        // we disable pacing for extremely large windows
117        if window > u64::from(u32::MAX) {
118            return None;
119        }
120
121        let window = window as u32;
122
123        let time_elapsed = now.checked_duration_since(self.prev).unwrap_or_else(|| {
124            warn!("received a timestamp early than a previous recorded time, ignoring");
125            Default::default()
126        });
127
128        if smoothed_rtt.as_nanos() == 0 {
129            return None;
130        }
131
132        let elapsed_rtts = time_elapsed.as_secs_f64() / smoothed_rtt.as_secs_f64();
133        let new_tokens = (window as f64 * 1.25 * elapsed_rtts).round() as u64;
134        self.tokens = self.tokens.saturating_add(new_tokens).min(self.capacity);
135
136        // In the unlikely event that we're getting polled faster than tokens are generated, ensure
137        // that `elapsed_rtts` can grow until we make progress.
138        if new_tokens > 0 {
139            self.prev = now;
140        }
141
142        // if we can already send a packet, there is no need for delay
143        if self.tokens >= bytes_to_send {
144            return None;
145        }
146
147        let unscaled_delay = smoothed_rtt
148            .checked_mul((bytes_to_send.max(self.capacity) - self.tokens) as _)
149            .unwrap_or(Duration::MAX)
150            / window;
151
152        // divisions come before multiplications to prevent overflow
153        // this is the time at which the pacing window becomes empty
154        Some((unscaled_delay / 5) * 4)
155    }
156
157    /// Return how long we need to wait before sending `bytes_to_send` when the congestion
158    /// controller dictates an explicit `pacing_rate` in bytes/sec.
159    ///
160    /// Credit accumulates in `tokens` at `pacing_rate` for the time elapsed since the last
161    /// refill, bounded by a burst budget derived from that same rate. If the credit on hand
162    /// is short, the returned delay indicates when the shortfall will have been earned.
163    fn delay_at_rate(
164        &mut self,
165        pacing_rate: u64,
166        bytes_to_send: u64,
167        mtu: u16,
168        now: Instant,
169    ) -> Option<Duration> {
170        // An explicit rate is clamped directly. The 1.25 correction in
171        // `rate_limited_window` exists only to cancel out the legacy refill speedup, which
172        // this path does not apply. A rate of zero would divide by zero below.
173        let rate = match self.max_bytes_per_second {
174            Some(max_bytes_per_second) => Ord::min(pacing_rate, max_bytes_per_second),
175            None => pacing_rate,
176        }
177        .max(1);
178
179        let capacity = rate_capacity(rate, mtu);
180        if capacity != self.capacity {
181            self.capacity = capacity;
182            // here we cap the number of bytes sent at once during a burst
183            self.tokens = self.capacity.min(self.tokens);
184        }
185        // Invalidate the window path's cache: its inputs no longer describe `capacity`.
186        self.last_window_inputs = None;
187
188        let time_elapsed = now.checked_duration_since(self.prev).unwrap_or_else(|| {
189            warn!("received a timestamp early than a previous recorded time, ignoring");
190            Default::default()
191        });
192        let new_tokens = (rate as f64 * time_elapsed.as_secs_f64()) as u64;
193
194        // Advance `prev` only once whole bytes have been earned, so elapsed time too short to
195        // pay for a single byte is carried over rather than discarded. Without this, a slow
196        // rate polled frequently would never accumulate anything.
197        if new_tokens > 0 {
198            self.tokens = self.tokens.saturating_add(new_tokens).min(self.capacity);
199            self.prev = now;
200        }
201
202        // Capped at the burst budget so that a `bytes_to_send` exceeding the whole bucket is
203        // still released eventually, rather than waiting for a level the bucket never reaches.
204        let target = Ord::min(bytes_to_send, self.capacity);
205        if self.tokens >= target {
206            return None;
207        }
208
209        // Wait for the shortfall only. Deriving the delay from `bytes_to_send` would re-arm
210        // the same interval on every poll and never retire, stalling the connection.
211        let deficit = target - self.tokens;
212        Some(Duration::from_secs_f64(deficit as f64 / rate as f64))
213    }
214}
215
216/// Calculates a pacer capacity for a pacing rate
217///
218/// Burst intervals trade distributing datagrams over time against waking the connection up more
219/// often than user-space timer accuracy can service; overshooting one by more than 25% loses the
220/// tokens for the extra elapsed time.
221fn rate_capacity(pacing_rate: u64, mtu: u16) -> u64 {
222    let mtu = u64::from(mtu);
223    let bytes_in =
224        |interval: Duration| ((pacing_rate as u128 * interval.as_nanos()) / 1_000_000_000) as u64;
225
226    let target_capacity = bytes_in(TARGET_BURST_INTERVAL);
227    // Never restrict capacity below one MTU.
228    let max_capacity = Ord::max(bytes_in(MAX_BURST_INTERVAL), mtu);
229
230    // Batch the greater of `TARGET_BURST_INTERVAL` or `MIN_BURST_SIZE` worth of traffic at a
231    // time, limited to at most `MAX_BURST_INTERVAL` worth to avoid inducing excessive latency.
232    Ord::min(
233        max_capacity,
234        target_capacity.clamp(MIN_BURST_SIZE * mtu, MAX_BURST_SIZE * mtu),
235    )
236}
237
238/// Calculates a pacer capacity for a certain window and RTT, which imply a rate
239fn optimal_capacity(smoothed_rtt: Duration, window: u64, mtu: u16) -> u64 {
240    let rtt = smoothed_rtt.as_nanos().max(1);
241    let rate = u64::try_from(window as u128 * 1_000_000_000 / rtt).unwrap_or(u64::MAX);
242    rate_capacity(rate, mtu)
243}
244
245/// Clamps the window to limit the sending rate to `max_bytes_per_second`.
246///
247/// If `max_bytes_per_second` is `None`, the original window is returned.
248fn rate_limited_window(
249    smoothed_rtt: Duration,
250    window: u64,
251    max_bytes_per_second: Option<u64>,
252) -> u64 {
253    let Some(max_bytes_per_second) = max_bytes_per_second else {
254        return window;
255    };
256
257    let rate_window = max_bytes_per_second as f64 * smoothed_rtt.as_secs_f64();
258
259    // the pacer refills tokens at x1.25 speed, so we shrink the window to cancel out the speedup
260    // (otherwise the actual sending rate could be higher than `max_bytes_per_second`)
261    let adjusted_rate_window = (rate_window / 1.25).round();
262
263    Ord::min(window, Ord::max(adjusted_rate_window as u64, 1))
264}
265
266/// Period of traffic to batch together on a reasonably fast connection
267const TARGET_BURST_INTERVAL: Duration = Duration::from_millis(2);
268
269/// Maximum period of traffic to batch together on a slow connection
270///
271/// Takes precedence over [`MIN_BURST_SIZE`].
272const MAX_BURST_INTERVAL: Duration = Duration::from_millis(10);
273
274/// Minimum number of datagrams to batch together, so long as we won't have to wait for more than
275/// [`MAX_BURST_INTERVAL`]
276const MIN_BURST_SIZE: u64 = 10;
277
278/// Creating 256 packets took 1ms in a benchmark, so larger bursts don't make sense.
279const MAX_BURST_SIZE: u64 = 256;
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    /// 100 Mbit/s in bytes/sec, the rate used by the controller-paced tests.
286    const TEST_PACING_RATE: u64 = 12_500_000;
287
288    /// Metrics from a controller that does not compute a rate of its own, as Cubic and Reno
289    /// report them.
290    fn unpaced_metrics(congestion_window: u64) -> ControllerMetrics {
291        ControllerMetrics {
292            congestion_window,
293            ..Default::default()
294        }
295    }
296
297    /// Metrics as a delay-based controller such as BBR3 reports them: both a pacing rate
298    /// and a send quantum are always present.
299    fn paced_metrics(
300        congestion_window: u64,
301        pacing_rate: u64,
302        send_quantum: u64,
303    ) -> ControllerMetrics {
304        ControllerMetrics {
305            congestion_window,
306            pacing_rate: Some(pacing_rate),
307            send_quantum: Some(send_quantum),
308            ..Default::default()
309        }
310    }
311
312    /// Polls `pacer` repeatedly at the single instant `now`, transmitting one `mtu`-sized
313    /// datagram each time it is allowed to, until it asks the caller to wait.
314    ///
315    /// Returns when the pacer wants to be polled again and the number of bytes
316    /// emitted before it blocked, or `None` if it never blocked.
317    fn burst_until_blocked(
318        pacer: &mut Pacer,
319        rtt: Duration,
320        mtu: u16,
321        now: Instant,
322        metrics: &ControllerMetrics,
323    ) -> Option<(Duration, u64)> {
324        let mut sent = 0;
325        for _ in 0..10_000 {
326            match pacer.delay(rtt, u64::from(mtu), mtu, now, metrics) {
327                Some(resume_after) => return Some((resume_after, sent)),
328                None => {
329                    pacer.on_transmit(mtu);
330                    sent += u64::from(mtu);
331                }
332            }
333        }
334        None
335    }
336
337    /// Drives an always-backlogged sender through `pacer` for `duration` of simulated time the
338    /// way `poll_transmit` does: send whenever the pacer allows it, otherwise jump to the instant
339    /// it asked to be polled again. Returns the bytes emitted.
340    fn bytes_sent_over(
341        pacer: &mut Pacer,
342        rtt: Duration,
343        mtu: u16,
344        start: Instant,
345        duration: Duration,
346        metrics: &ControllerMetrics,
347    ) -> u64 {
348        /// Guards against a pacer that never advances time; far above the ~8k polls a correct
349        /// pacer needs for one second at [`TEST_PACING_RATE`].
350        const MAX_POLLS: u64 = 1_000_000;
351
352        let deadline = start + duration;
353        let mut at = start;
354        let mut sent = 0;
355        let mut polls = 0;
356        while at < deadline {
357            polls += 1;
358            assert!(
359                polls < MAX_POLLS,
360                "pacer made no progress: {sent} bytes emitted without reaching the deadline"
361            );
362            match pacer.delay(rtt, u64::from(mtu), mtu, at, metrics) {
363                None => {
364                    pacer.on_transmit(mtu);
365                    sent += u64::from(mtu);
366                }
367                Some(resume) => at += resume,
368            }
369        }
370        sent
371    }
372
373    #[test]
374    fn blocks_greedy_sender_at_controller_pacing_rate() {
375        let mtu = 1500;
376        let rtt = Duration::from_millis(50);
377        let window = 2_000_000;
378        let now = Instant::now();
379        // `send_quantum` at BBR3's `2 * SMSS` floor, i.e. what it reports at low rates.
380        let metrics = paced_metrics(window, TEST_PACING_RATE, 2 * u64::from(mtu));
381        let mut pacer = Pacer::new(rtt, window, mtu, None, now);
382
383        // Honouring a finite rate is only possible by delaying, so a sender polling at a
384        // single instant must eventually be told to wait.
385        assert!(
386            burst_until_blocked(&mut pacer, rtt, mtu, now, &metrics).is_some(),
387            "pacer never blocked while polled at a single instant, so the controller's \
388             pacing rate is not being enforced"
389        );
390    }
391
392    #[test]
393    fn pacing_delay_unblocks_once_it_expires() {
394        let mtu = 1500;
395        let rtt = Duration::from_millis(50);
396        let window = 2_000_000;
397        let now = Instant::now();
398        let metrics = paced_metrics(window, TEST_PACING_RATE, 2 * u64::from(mtu));
399        let mut pacer = Pacer::new(rtt, window, mtu, None, now);
400
401        let (resume_after, _) = burst_until_blocked(&mut pacer, rtt, mtu, now, &metrics)
402            .expect("pacer must block once the burst budget is spent");
403
404        // `poll_transmit` re-runs when the pacing timer fires. If the pacer re-derives the
405        // same delay from the new `now` it would re-arm forever and the connection stalls.
406        assert_eq!(
407            pacer.delay(rtt, u64::from(mtu), mtu, now + resume_after, &metrics),
408            None,
409            "the delay the pacer asked for must be long enough to unblock the send"
410        );
411    }
412
413    #[test]
414    fn aggregate_throughput_matches_controller_pacing_rate() {
415        const SECONDS: u64 = 1;
416
417        let mtu = 1500;
418        let rtt = Duration::from_millis(50);
419        let window = 2_000_000;
420        let metrics = paced_metrics(window, TEST_PACING_RATE, 2 * u64::from(mtu));
421        let start = Instant::now();
422        let mut pacer = Pacer::new(rtt, window, mtu, None, start);
423
424        let sent = bytes_sent_over(
425            &mut pacer,
426            rtt,
427            mtu,
428            start,
429            Duration::from_secs(SECONDS),
430            &metrics,
431        );
432
433        let expected = TEST_PACING_RATE * SECONDS;
434        // Slack covers the bucket the pacer starts full plus the trailing partial burst.
435        let slack = expected / 20;
436        assert!(
437            sent <= expected + slack,
438            "emitted {sent} bytes in {SECONDS}s, but pacing_rate allows only {expected}"
439        );
440        assert!(
441            sent + slack >= expected,
442            "emitted {sent} bytes in {SECONDS}s, underrunning pacing_rate {expected}"
443        );
444    }
445
446    #[test]
447    fn does_not_panic_on_bad_instant() {
448        let old_instant = Instant::now();
449        let new_instant = old_instant + Duration::from_micros(15);
450        let rtt = Duration::from_micros(400);
451
452        assert!(
453            Pacer::new(rtt, 30000, 1500, None, new_instant)
454                .delay(
455                    Duration::from_micros(0),
456                    0,
457                    1500,
458                    old_instant,
459                    &unpaced_metrics(1),
460                )
461                .is_none()
462        );
463        assert!(
464            Pacer::new(rtt, 30000, 1500, None, new_instant)
465                .delay(
466                    Duration::from_micros(0),
467                    1600,
468                    1500,
469                    old_instant,
470                    &unpaced_metrics(1),
471                )
472                .is_none()
473        );
474        assert!(
475            Pacer::new(rtt, 30000, 1500, None, new_instant)
476                .delay(
477                    Duration::from_micros(0),
478                    1500,
479                    1500,
480                    old_instant,
481                    &unpaced_metrics(3000),
482                )
483                .is_none()
484        );
485    }
486
487    #[test]
488    fn derives_initial_capacity() {
489        let window = 2_000_000;
490        let mtu = 1500;
491        let rtt = Duration::from_millis(50);
492        let now = Instant::now();
493
494        let pacer = Pacer::new(rtt, window, mtu, None, now);
495        assert_eq!(
496            pacer.capacity,
497            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
498        );
499        assert_eq!(pacer.tokens, pacer.capacity);
500
501        let pacer = Pacer::new(Duration::from_millis(0), window, mtu, None, now);
502        assert_eq!(pacer.capacity, MAX_BURST_SIZE * mtu as u64);
503        assert_eq!(pacer.tokens, pacer.capacity);
504
505        let pacer = Pacer::new(rtt, 1, mtu, None, now);
506        assert_eq!(pacer.capacity, mtu as u64);
507        assert_eq!(pacer.tokens, pacer.capacity);
508    }
509
510    #[test]
511    fn adjusts_capacity() {
512        let window = 2_000_000;
513        let mtu = 1500;
514        let rtt = Duration::from_millis(50);
515        let now = Instant::now();
516
517        let mut pacer = Pacer::new(rtt, window, mtu, None, now);
518        assert_eq!(
519            pacer.capacity,
520            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
521        );
522        assert_eq!(pacer.tokens, pacer.capacity);
523        let initial_tokens = pacer.tokens;
524
525        pacer.delay(rtt, mtu as u64, mtu, now, &unpaced_metrics(window * 2));
526        assert_eq!(
527            pacer.capacity,
528            (2 * window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
529        );
530        assert_eq!(pacer.tokens, initial_tokens);
531
532        pacer.delay(rtt, mtu as u64, mtu, now, &unpaced_metrics(window / 2));
533        assert_eq!(
534            pacer.capacity,
535            (window as u128 / 2 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
536        );
537        assert_eq!(pacer.tokens, initial_tokens / 2);
538
539        pacer.delay(rtt, mtu as u64, mtu * 2, now, &unpaced_metrics(window));
540        assert_eq!(
541            pacer.capacity,
542            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
543        );
544
545        pacer.delay(rtt, mtu as u64, 20_000, now, &unpaced_metrics(window));
546        assert_eq!(pacer.capacity, 20_000_u64 * MIN_BURST_SIZE);
547    }
548
549    #[test]
550    fn computes_pause_correctly() {
551        let window = 2_000_000u64;
552        let mtu = 1000;
553        let rtt = Duration::from_millis(50);
554        let old_instant = Instant::now();
555
556        let mut pacer = Pacer::new(rtt, window, mtu, None, old_instant);
557        let packet_capacity = pacer.capacity / mtu as u64;
558
559        for _ in 0..packet_capacity {
560            assert_eq!(
561                pacer.delay(rtt, mtu as u64, mtu, old_instant, &unpaced_metrics(window)),
562                None,
563                "When capacity is available packets should be sent immediately"
564            );
565
566            pacer.on_transmit(mtu);
567        }
568
569        let pace_duration = Duration::from_nanos((TARGET_BURST_INTERVAL.as_nanos() * 4 / 5) as u64);
570
571        let actual_delay = pacer
572            .delay(rtt, mtu as u64, mtu, old_instant, &unpaced_metrics(window))
573            .expect("Send must be delayed");
574
575        let diff = actual_delay.abs_diff(pace_duration);
576
577        // Allow up to 2ns difference due to rounding
578        assert!(
579            diff < Duration::from_nanos(2),
580            "expected ≈ {pace_duration:?}, got {actual_delay:?} (diff {diff:?})"
581        );
582        // Refill half of the tokens
583        assert_eq!(
584            pacer.delay(
585                rtt,
586                mtu as u64,
587                mtu,
588                old_instant + pace_duration / 2,
589                &unpaced_metrics(window),
590            ),
591            None
592        );
593        assert_eq!(pacer.tokens, pacer.capacity / 2);
594
595        for _ in 0..packet_capacity / 2 {
596            assert_eq!(
597                pacer.delay(rtt, mtu as u64, mtu, old_instant, &unpaced_metrics(window)),
598                None,
599                "When capacity is available packets should be sent immediately"
600            );
601
602            pacer.on_transmit(mtu);
603        }
604
605        // Refill all capacity by waiting more than the expected duration
606        assert_eq!(
607            pacer.delay(
608                rtt,
609                mtu as u64,
610                mtu,
611                old_instant + pace_duration * 3 / 2,
612                &unpaced_metrics(window),
613            ),
614            None
615        );
616        assert_eq!(pacer.tokens, pacer.capacity);
617    }
618
619    #[test]
620    fn computes_pause_correctly_for_rate_limited() {
621        let window = 2_000_000u64;
622        let mtu = 1000;
623        let rtt = Duration::from_millis(50);
624        let old_instant = Instant::now();
625
626        let mut pacer = Pacer::new(rtt, window, mtu, Some(2_000), old_instant);
627        assert_eq!(
628            pacer.delay(rtt, 1_000, mtu, old_instant, &unpaced_metrics(window)),
629            None,
630            "When capacity is available packets should be sent immediately"
631        );
632        pacer.on_transmit(mtu);
633
634        let actual_delay = pacer
635            .delay(rtt, 1_000, mtu, old_instant, &unpaced_metrics(window))
636            .expect("Send must be delayed");
637
638        let expected_delay = Duration::from_millis(500);
639        let diff = actual_delay.abs_diff(expected_delay);
640
641        // Allow up to 2ns difference due to rounding
642        assert!(
643            diff < Duration::from_nanos(2),
644            "expected ≈ {expected_delay:?}, got {actual_delay:?} (diff {diff:?})"
645        );
646
647        // Should be able to send after a while
648        let now = old_instant + expected_delay / 2;
649        assert_eq!(
650            pacer.delay(rtt, 500, mtu, now, &unpaced_metrics(window)),
651            None
652        );
653    }
654
655    #[test]
656    fn derives_burst_budget_from_controller_pacing_rate() {
657        let window = 2_000_000;
658        let mtu = 1500;
659        let rtt = Duration::from_millis(50);
660        let now = Instant::now();
661        // A window twice the one the pacer was built with must not disturb a budget the
662        // controller's rate determines.
663        let metrics = paced_metrics(window * 2, TEST_PACING_RATE, 2 * u64::from(mtu));
664        let mut pacer = Pacer::new(rtt, window, mtu, None, now);
665
666        pacer.delay(rtt, u64::from(mtu), mtu, now, &metrics);
667
668        assert_eq!(pacer.capacity, rate_capacity(TEST_PACING_RATE, mtu));
669        // 2ms of traffic at 100 Mbit/s, i.e. `TARGET_BURST_INTERVAL` worth.
670        assert_eq!(pacer.capacity, 25_000);
671    }
672
673    #[test]
674    fn pacing_delay_covers_exactly_the_token_shortfall() {
675        // 200 MB/s in bytes/s
676        const RATE: u64 = 200_000_000;
677
678        let window = 2_000_000;
679        let mtu = 1500;
680        let rtt = Duration::from_millis(50);
681        let now = Instant::now();
682        let metrics = paced_metrics(window, RATE, 2 * u64::from(mtu));
683        let mut pacer = Pacer::new(rtt, window, mtu, None, now);
684
685        let (resume_after, _) = burst_until_blocked(&mut pacer, rtt, mtu, now, &metrics)
686            .expect("pacer must block once the burst budget is spent");
687
688        // The wait pays for the credit still missing, not for the whole datagram: charging
689        // for bytes already covered by tokens on hand would pace below `pacing_rate`.
690        let deficit = u64::from(mtu) - pacer.tokens;
691        assert_eq!(
692            resume_after,
693            Duration::from_secs_f64(deficit as f64 / RATE as f64)
694        );
695    }
696
697    #[test]
698    fn burst_is_bounded_by_the_target_interval() {
699        let window = 2_000_000;
700        let mtu = 1500;
701        let rtt = Duration::from_millis(50);
702        let now = Instant::now();
703        let metrics = paced_metrics(window, TEST_PACING_RATE, 2 * u64::from(mtu));
704        let mut pacer = Pacer::new(rtt, window, mtu, None, now);
705
706        let (_, burst) = burst_until_blocked(&mut pacer, rtt, mtu, now, &metrics)
707            .expect("pacer must block once the burst budget is spent");
708
709        // Bursting more than `TARGET_BURST_INTERVAL` worth of traffic is what fills bottleneck
710        // queues; falling far short of it wakes the connection up more often than the timer can
711        // service. One datagram of slack either way is inherent in releasing whole datagrams.
712        let budget = rate_capacity(TEST_PACING_RATE, mtu);
713        assert!(
714            burst <= budget + u64::from(mtu),
715            "burst of {burst} bytes overshoots the {budget} byte budget by over one datagram"
716        );
717        assert!(
718            burst + u64::from(mtu) >= budget,
719            "burst of {burst} bytes undershoots the {budget} byte budget by over one datagram"
720        );
721    }
722
723    #[test]
724    fn shrinks_burst_budget_when_pacing_rate_drops() {
725        let window = 2_000_000;
726        let mtu = 1500;
727        let rtt = Duration::from_millis(50);
728        let now = Instant::now();
729        let quantum = 2 * u64::from(mtu);
730        let fast = paced_metrics(window, TEST_PACING_RATE, quantum);
731        let slow = paced_metrics(window, TEST_PACING_RATE / 10, quantum);
732        let mut pacer = Pacer::new(rtt, window, mtu, None, now);
733
734        // Earn credit at the high rate, as during a ProbeBW_UP phase...
735        assert_eq!(pacer.delay(rtt, u64::from(mtu), mtu, now, &fast), None);
736        assert_eq!(pacer.capacity, rate_capacity(TEST_PACING_RATE, mtu));
737
738        // ...then a gain change lowers it. Credit earned at the old rate must not survive as a
739        // burst the new rate cannot pay for.
740        pacer.delay(rtt, u64::from(mtu), mtu, now, &slow);
741
742        let budget = rate_capacity(TEST_PACING_RATE / 10, mtu);
743        assert_eq!(pacer.capacity, budget);
744        assert!(
745            pacer.tokens <= budget,
746            "{} tokens outlive the {budget} byte budget of the lowered rate",
747            pacer.tokens
748        );
749    }
750
751    #[test]
752    fn rate_path_does_not_leave_stale_window_capacity() {
753        let window = 2_000_000;
754        let mtu = 1500;
755        let rtt = Duration::from_millis(50);
756        let now = Instant::now();
757        let mut pacer = Pacer::new(rtt, window, mtu, None, now);
758
759        // A controller free to report a rate on some calls and not on others is within what
760        // `ControllerMetrics` allows. The rate path leaves its own budget behind...
761        pacer.delay(
762            rtt,
763            u64::from(mtu),
764            mtu,
765            now,
766            &paced_metrics(window, TEST_PACING_RATE, 2 * u64::from(mtu)),
767        );
768        assert_eq!(pacer.capacity, rate_capacity(TEST_PACING_RATE, mtu));
769
770        // ...so the window path must not mistake it for a budget of its own, even though
771        // neither the window nor the MTU it keys on has changed.
772        pacer.delay(rtt, u64::from(mtu), mtu, now, &unpaced_metrics(window));
773
774        assert_eq!(
775            pacer.capacity,
776            optimal_capacity(rtt, window, mtu),
777            "the window path kept a burst budget the rate path derived"
778        );
779    }
780
781    #[test]
782    fn max_bytes_per_second_overrides_a_higher_controller_rate() {
783        const SECONDS: u64 = 1;
784        /// 1 Mbit/s in bytes/sec, two orders of magnitude under [`TEST_PACING_RATE`].
785        const LIMIT: u64 = 125_000;
786
787        let window = 2_000_000;
788        let mtu = 1500;
789        let rtt = Duration::from_millis(50);
790        let start = Instant::now();
791        let metrics = paced_metrics(window, TEST_PACING_RATE, 2 * u64::from(mtu));
792        let mut pacer = Pacer::new(rtt, window, mtu, Some(LIMIT), start);
793
794        let sent = bytes_sent_over(
795            &mut pacer,
796            rtt,
797            mtu,
798            start,
799            Duration::from_secs(SECONDS),
800            &metrics,
801        );
802
803        // The configured ceiling binds even though the controller asks for far more.
804        let expected = LIMIT * SECONDS;
805        let slack = expected / 20;
806        assert!(
807            sent <= expected + slack,
808            "emitted {sent} bytes in {SECONDS}s, over the {expected} byte ceiling"
809        );
810        assert!(
811            sent + slack >= expected,
812            "emitted {sent} bytes in {SECONDS}s, underrunning the {expected} byte ceiling"
813        );
814    }
815}