Skip to content

Commit a19f148

Browse files
feat(s2n-quic-core): add congestion control events (#1521)
* feat(s2n-quic-core): add congestion controller events * Wire up on_slow_start_exited * Remove is_slow_start from congestion controller trait * Add delivery rate and pacing rate events * New snapshot results * Adjust ranges in fuzz test * add pacing fuzz test corpus * Make congestion_controller::Publisher a trait and other PR feedback * Fix formatting * Change delivery_rate_updated to delivery_rate_sampled and add all rate sample fields
1 parent da677fa commit a19f148

File tree

63 files changed

+1367
-275
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1367
-275
lines changed

quic/s2n-quic-core/src/event.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ ident_into_event!(
5353
i64,
5454
usize,
5555
isize,
56+
f32,
5657
Duration,
5758
bool,
5859
connection::Error,

quic/s2n-quic-core/src/event/generated.rs

Lines changed: 299 additions & 11 deletions
Large diffs are not rendered by default.

quic/s2n-quic-core/src/recovery/bandwidth/estimator.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
use crate::time::Timestamp;
4+
use crate::{event, event::IntoEvent, recovery::congestion_controller::Publisher, time::Timestamp};
55
use core::{
66
cmp::{max, Ordering},
77
time::Duration,
@@ -62,6 +62,17 @@ impl Bandwidth {
6262
}
6363
}
6464
}
65+
66+
/// Represents the bandwidth as bytes per second
67+
pub fn as_bytes_per_second(&self) -> u64 {
68+
const ONE_SECOND_IN_NANOS: u64 = Duration::from_secs(1).as_nanos() as u64;
69+
70+
if *self == Bandwidth::INFINITY {
71+
return u64::MAX;
72+
}
73+
74+
ONE_SECOND_IN_NANOS / self.nanos_per_byte
75+
}
6576
}
6677

6778
impl Default for Bandwidth {
@@ -170,6 +181,23 @@ impl RateSample {
170181
}
171182
}
172183

184+
impl IntoEvent<event::builder::RateSample> for RateSample {
185+
fn into_event(self) -> event::builder::RateSample {
186+
event::builder::RateSample {
187+
interval: self.interval.into_event(),
188+
delivered_bytes: self.delivered_bytes.into_event(),
189+
lost_bytes: self.lost_bytes.into_event(),
190+
ecn_ce_count: self.ecn_ce_count.into_event(),
191+
is_app_limited: self.is_app_limited.into_event(),
192+
prior_delivered_bytes: self.prior_delivered_bytes.into_event(),
193+
bytes_in_flight: self.bytes_in_flight.into_event(),
194+
prior_lost_bytes: self.prior_lost_bytes.into_event(),
195+
prior_ecn_ce_count: self.prior_ecn_ce_count.into_event(),
196+
delivery_rate_bytes_per_second: self.delivery_rate().as_bytes_per_second().into_event(),
197+
}
198+
}
199+
}
200+
173201
/// Bandwidth estimator as defined in [Delivery Rate Estimation](https://datatracker.ietf.org/doc/draft-cheng-iccrg-delivery-rate-estimation/)
174202
/// and [BBR Congestion Control](https://datatracker.ietf.org/doc/draft-cardwell-iccrg-bbr-congestion-control/).
175203
#[derive(Clone, Debug, Default)]
@@ -270,12 +298,13 @@ impl Estimator {
270298
//# rate sample based on a snapshot of connection delivery information from the time
271299
//# at which the packet was last transmitted.
272300
/// Called for each acknowledgement of one or more packets
273-
pub fn on_ack(
301+
pub fn on_ack<Pub: Publisher>(
274302
&mut self,
275303
bytes_acknowledged: usize,
276304
newest_acked_time_sent: Timestamp,
277305
newest_acked_packet_info: PacketInfo,
278306
now: Timestamp,
307+
publisher: &mut Pub,
279308
) {
280309
self.delivered_bytes += bytes_acknowledged as u64;
281310
self.delivered_time = Some(now);
@@ -318,6 +347,8 @@ impl Estimator {
318347
// so the values are up to date even when no loss or ECN CE markings are received.
319348
self.rate_sample.lost_bytes = self.lost_bytes - self.rate_sample.prior_lost_bytes;
320349
self.rate_sample.ecn_ce_count = self.ecn_ce_count - self.rate_sample.prior_ecn_ce_count;
350+
351+
publisher.on_delivery_rate_sampled(self.rate_sample);
321352
}
322353

323354
/// Called when packets are declared lost
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
source: quic/s2n-quic-core/src/recovery/bandwidth/estimator/tests.rs
3+
expression: ""
4+
---
5+
DeliveryRateSampled { path_id: 0, rate_sample: RateSample { interval: 1s, delivered_bytes: 1500, lost_bytes: 100, ecn_ce_count: 5, is_app_limited: false, prior_delivered_bytes: 15000, bytes_in_flight: 1500, prior_lost_bytes: 0, prior_ecn_ce_count: 0, delivery_rate_bytes_per_second: 1500 } }
6+
DeliveryRateSampled { path_id: 0, rate_sample: RateSample { interval: 1s, delivered_bytes: 1501, lost_bytes: 100, ecn_ce_count: 5, is_app_limited: false, prior_delivered_bytes: 15000, bytes_in_flight: 1500, prior_lost_bytes: 0, prior_ecn_ce_count: 0, delivery_rate_bytes_per_second: 1501 } }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
source: quic/s2n-quic-core/src/recovery/bandwidth/estimator/tests.rs
3+
expression: ""
4+
---
5+
DeliveryRateSampled { path_id: 0, rate_sample: RateSample { interval: 4s, delivered_bytes: 1500, lost_bytes: 0, ecn_ce_count: 0, is_app_limited: false, prior_delivered_bytes: 0, bytes_in_flight: 0, prior_lost_bytes: 0, prior_ecn_ce_count: 0, delivery_rate_bytes_per_second: 375 } }
6+
DeliveryRateSampled { path_id: 0, rate_sample: RateSample { interval: 5s, delivered_bytes: 1500, lost_bytes: 0, ecn_ce_count: 0, is_app_limited: false, prior_delivered_bytes: 1500, bytes_in_flight: 1500, prior_lost_bytes: 0, prior_ecn_ce_count: 0, delivery_rate_bytes_per_second: 300 } }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
source: quic/s2n-quic-core/src/recovery/bandwidth/estimator/tests.rs
3+
expression: ""
4+
---
5+
DeliveryRateSampled { path_id: 0, rate_sample: RateSample { interval: 10s, delivered_bytes: 201500, lost_bytes: 150, ecn_ce_count: 15, is_app_limited: false, prior_delivered_bytes: 0, bytes_in_flight: 0, prior_lost_bytes: 0, prior_ecn_ce_count: 0, delivery_rate_bytes_per_second: 20150 } }
6+
DeliveryRateSampled { path_id: 0, rate_sample: RateSample { interval: 11s, delivered_bytes: 3000, lost_bytes: 0, ecn_ce_count: 0, is_app_limited: true, prior_delivered_bytes: 200000, bytes_in_flight: 3000, prior_lost_bytes: 150, prior_ecn_ce_count: 15, delivery_rate_bytes_per_second: 272 } }
7+
DeliveryRateSampled { path_id: 0, rate_sample: RateSample { interval: 11s, delivered_bytes: 4500, lost_bytes: 0, ecn_ce_count: 0, is_app_limited: true, prior_delivered_bytes: 200000, bytes_in_flight: 3000, prior_lost_bytes: 150, prior_ecn_ce_count: 15, delivery_rate_bytes_per_second: 409 } }

quic/s2n-quic-core/src/recovery/bandwidth/estimator/tests.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
use super::*;
5-
use crate::time::{Clock, NoopClock};
5+
use crate::{
6+
event, path,
7+
recovery::congestion_controller::PathPublisher,
8+
time::{Clock, NoopClock},
9+
};
610

711
#[test]
812
fn bandwidth() {
@@ -138,6 +142,8 @@ fn on_packet_sent() {
138142

139143
#[test]
140144
fn app_limited() {
145+
let mut publisher = event::testing::Publisher::snapshot();
146+
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
141147
let first_sent_time = NoopClock.get_time();
142148
let delivered_time = first_sent_time + Duration::from_secs(1);
143149
let mut bw_estimator = Estimator {
@@ -176,18 +182,32 @@ fn app_limited() {
176182
};
177183

178184
// Acknowledge all the bytes that were inflight when the app-limited period began
179-
bw_estimator.on_ack(1500, delivered_time, packet_info, delivered_time);
185+
bw_estimator.on_ack(
186+
1500,
187+
delivered_time,
188+
packet_info,
189+
delivered_time,
190+
&mut publisher,
191+
);
180192
// Still app_limited, since we need bytes to be acknowledged after the app limited period
181193
assert_eq!(Some(1500 + 15000), bw_estimator.app_limited_delivered_bytes);
182194

183195
// Acknowledge one more byte
184-
bw_estimator.on_ack(1, delivered_time, packet_info, delivered_time);
196+
bw_estimator.on_ack(
197+
1,
198+
delivered_time,
199+
packet_info,
200+
delivered_time,
201+
&mut publisher,
202+
);
185203
// Now the app limited period is over
186204
assert_eq!(None, bw_estimator.app_limited_delivered_bytes);
187205
}
188206

189207
#[test]
190208
fn on_packet_ack_rate_sample() {
209+
let mut publisher = event::testing::Publisher::snapshot();
210+
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
191211
let t0 = NoopClock.get_time() + Duration::from_secs(60);
192212
let t1 = t0 + Duration::from_secs(1);
193213
let t2 = t0 + Duration::from_secs(2);
@@ -207,7 +227,7 @@ fn on_packet_ack_rate_sample() {
207227

208228
let now = t0 + Duration::from_secs(10);
209229
let delivered_bytes = bw_estimator.delivered_bytes;
210-
bw_estimator.on_ack(1500, t0, packet_1, now);
230+
bw_estimator.on_ack(1500, t0, packet_1, now, &mut publisher);
211231

212232
assert_eq!(bw_estimator.delivered_bytes, delivered_bytes + 1500);
213233
assert_eq!(bw_estimator.delivered_time, Some(now));
@@ -247,7 +267,7 @@ fn on_packet_ack_rate_sample() {
247267
// Ack a newer packet
248268
let now = now + Duration::from_secs(1);
249269
let delivered_bytes = bw_estimator.delivered_bytes;
250-
bw_estimator.on_ack(1500, t2, packet_3, now);
270+
bw_estimator.on_ack(1500, t2, packet_3, now, &mut publisher);
251271

252272
assert_eq!(bw_estimator.delivered_bytes, delivered_bytes + 1500);
253273
assert_eq!(bw_estimator.delivered_time, Some(now));
@@ -283,7 +303,7 @@ fn on_packet_ack_rate_sample() {
283303
// Ack an older packet
284304
let now = now + Duration::from_secs(1);
285305
let delivered_bytes = bw_estimator.delivered_bytes;
286-
bw_estimator.on_ack(1500, t1, packet_2, now);
306+
bw_estimator.on_ack(1500, t1, packet_2, now, &mut publisher);
287307

288308
assert_eq!(bw_estimator.delivered_bytes, delivered_bytes + 1500);
289309
assert_eq!(bw_estimator.delivered_time, Some(now));
@@ -327,19 +347,27 @@ fn on_packet_ack_rate_sample() {
327347
//# by capping the delivery rate sample to be no higher than the send rate.
328348
#[test]
329349
fn on_packet_ack_implausible_ack_rate() {
350+
let mut publisher = event::testing::Publisher::snapshot();
351+
let mut publisher = PathPublisher::new(&mut publisher, path::Id::test_id());
330352
let t0 = NoopClock.get_time();
331353
let mut bw_estimator = Estimator::default();
332354

333355
// A packet is sent and acknowledged 4 seconds later
334356
let packet_info = bw_estimator.on_packet_sent(0, Some(false), t0);
335357
let t4 = t0 + Duration::from_secs(4);
336-
bw_estimator.on_ack(1500, t0, packet_info, t4);
358+
bw_estimator.on_ack(1500, t0, packet_info, t4, &mut publisher);
337359

338360
// A packet is sent and acknowledged 1 second later
339361
let t5 = t0 + Duration::from_secs(5);
340362
let packet_info = bw_estimator.on_packet_sent(1500, Some(false), t5);
341363
let now = t0 + Duration::from_secs(6);
342-
bw_estimator.on_ack(1500, t0 + Duration::from_secs(5), packet_info, now);
364+
bw_estimator.on_ack(
365+
1500,
366+
t0 + Duration::from_secs(5),
367+
packet_info,
368+
now,
369+
&mut publisher,
370+
);
343371

344372
let send_elapsed = t5 - packet_info.first_sent_time;
345373
let ack_elapsed = now - packet_info.delivered_time;
@@ -388,3 +416,12 @@ fn on_explicit_congestion() {
388416
assert_eq!(8, bw_estimator.ecn_ce_count);
389417
assert_eq!(8, bw_estimator.rate_sample.ecn_ce_count);
390418
}
419+
420+
#[test]
421+
fn as_bytes_per_second() {
422+
let bandwidth = Bandwidth::new(10_000, Duration::from_secs(1));
423+
424+
assert_eq!(10_000, bandwidth.as_bytes_per_second());
425+
assert_eq!(0, Bandwidth::ZERO.as_bytes_per_second());
426+
assert_eq!(u64::MAX, Bandwidth::INFINITY.as_bytes_per_second());
427+
}

0 commit comments

Comments
 (0)