-
-
Notifications
You must be signed in to change notification settings - Fork 384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: ack receiver timestamps #1992
base: main
Are you sure you want to change the base?
feat: ack receiver timestamps #1992
Conversation
Surfaces the TransportParameters to the application
Adds ReceiverTimestamps data structure to the space and expose methods.
This commit processes the inbound ACK frames that contain timestamps.
This commit handles sending frames that contain timestamps
Surfaces timing information to the CC interface.
I don't think a feature makes sense for this, what's the motivation for that? Suggest squashing all of these commits because I don't think they make much sense on their own. |
@djc I suggested using a feature bc it is a draft specification, perhaps we should name the feature after the specification |
I don't think that's a good reason to make it a feature. The current API surface will still become part of our public API, and it just adds a bunch of complexity. We've added draft functionality before, should be fine. |
I can remove the feature flag in a follow up commit. Regarding squashing, my intention for the commits were to keep separate different parts of the PR for review and then squash at the end. If you want me to squash it now, I can do that too. |
While we are fans of clean commit histories with atomic commits, I think this falls short of that because the commits are not conceptually independent -- it doesn't make sense to add configuration that doesn't do anything, for example. |
f7bd14c
to
3af9df4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a bunch of mostly stylistic feedback.
@@ -34,6 +34,20 @@ pub trait Controller: Send + Sync { | |||
) { | |||
} | |||
|
|||
#[allow(unused_variables)] | |||
/// Packet deliveries were confirmed with timestamps information. | |||
fn on_ack_packet( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these actually changes you'd look to make to the on_ack()
interface which you're adding a separate method for to keep it semver-compatible?
If so, should this default implementation call self.on_ack()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I did it this way to prevent any breaking changes if there were users of the on_ack
method. Yea, I think it makes sense for the default implementation to all self.on_ack
, and then we can remove the congestion.on_ack
call from on_packet_acked
and just have it call congestion.on_ack_packet
.
self.next_pn -= 1; | ||
} | ||
|
||
let delta = self.data.get_var().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this safe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This follows the same pattern done on AckIter
, where unwrap
is used.
I think it's considered safe because this is defined in the spec. If the value is expected to be a VARINT based on the spec but it isn't, we wouldn't be able to proceed in processing the packet.
Another option is to change the return type to be a tuple that includes an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't include a SAFETY comment here because the others didn't have them either.
receive_timestamps_exponent: config | ||
.ack_timestamp_config | ||
.as_ref() | ||
.map(|cfg| cfg.exponent), | ||
|
||
max_recv_timestamps_per_ack: config | ||
.ack_timestamp_config | ||
.as_ref() | ||
.map(|cfg| cfg.max_timestamps_per_ack), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these maybe shouldn't be separate Option
s, but a tuple wrapped in a single Option
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this file, rather than using two individual parameters on the TransportParameters
struct, I'm using the AckTimestampConfig
and making max_timestamps_per_ack
an Option
. None
signaling that the feature is disabled.
Exponent defaults to 0, which is what the spec states.
It could be worth proposing for the draft that the transport parameters related to this feature are grouped together similar to how PreferredAddress
does it.
change field name and arg names
c9224f3
to
67af041
Compare
@@ -343,6 +347,7 @@ pub struct Ack { | |||
pub delay: u64, | |||
pub additional: Bytes, | |||
pub ecn: Option<EcnCounts>, | |||
pub timestamps: Option<Bytes>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I experimented on having a separate struct for AckWithTimestamps
, but the primary function, on_ack_received
, that uses Ack
is complex and it was easier to just add the timestamps here.
receive_timestamps_exponent: config | ||
.ack_timestamp_config | ||
.as_ref() | ||
.map(|cfg| cfg.exponent), | ||
|
||
max_recv_timestamps_per_ack: config | ||
.ack_timestamp_config | ||
.as_ref() | ||
.map(|cfg| cfg.max_timestamps_per_ack), | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this file, rather than using two individual parameters on the TransportParameters
struct, I'm using the AckTimestampConfig
and making max_timestamps_per_ack
an Option
. None
signaling that the feature is disabled.
Exponent defaults to 0, which is what the spec states.
It could be worth proposing for the draft that the transport parameters related to this feature are grouped together similar to how PreferredAddress
does it.
self.next_pn -= 1; | ||
} | ||
|
||
let delta = self.data.get_var().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This follows the same pattern done on AckIter
, where unwrap
is used.
I think it's considered safe because this is defined in the spec. If the value is expected to be a VARINT based on the spec but it isn't, we wouldn't be able to proceed in processing the packet.
Another option is to change the return type to be a tuple that includes an error.
@@ -34,6 +34,20 @@ pub trait Controller: Send + Sync { | |||
) { | |||
} | |||
|
|||
#[allow(unused_variables)] | |||
/// Packet deliveries were confirmed with timestamps information. | |||
fn on_ack_packet( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I did it this way to prevent any breaking changes if there were users of the on_ack
method. Yea, I think it makes sense for the default implementation to all self.on_ack
, and then we can remove the congestion.on_ack
call from on_packet_acked
and just have it call congestion.on_ack_packet
.
fn new(largest: u64, basis_instant: Instant, exponent: u64, mut data: &'a [u8]) -> Self { | ||
// We read and throw away the Timestamp Range Count value because | ||
// it was already used to properly slice the data. | ||
let _ = data.get_var().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This unwrap
is safe because the data
byte slice was sliced using scan_ack_timestamp_blocks
, which this get_var()
was checked using ?
.
self.next_pn -= 1; | ||
} | ||
|
||
let delta = self.data.get_var().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't include a SAFETY comment here because the others didn't have them either.
fn new(largest: u64, basis_instant: Instant, exponent: u64, mut data: &'a [u8]) -> Self { | ||
// We read and throw away the Timestamp Range Count value because | ||
// it was already used to properly slice the data. | ||
let _ = data.get_var().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a safety comment.
4a42bec
to
fa004c5
Compare
This PR is the implementation that addresses #1988
Manual E2E Testing/Validation instructions: sterlingdeng#2