-
-
Notifications
You must be signed in to change notification settings - Fork 396
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
ACK Receive Timestamps #1988
Comments
Haven't looked at the PR yet, but initial thoughts from this overview: Transport parameter and frame IDs should be reserved through the IETF before merging; use whatever you like for private testing. If it's too early to reserve values it's probably too easy to include in a public, stable QUIC implementation, but I understand the bar for reserving a value to be very low. Maybe Meta already has values picked out?
According to the draft you cite, the basis is not a transport parameter, or communicated at all. The extension seems to only communicate inter-packet delays, not absolute time.
This is tricky because packets can be interleaved between spaces, complicating conversion from actual packet numbers (as in an ACK frame) to this counter. We could use a shared packet number space to begin with (never reusing packet numbers between spaces), or just pass the space down to the controller to disambiguate.
Because the basis is not communicated, send/receive times are not comparable. This should be represented in the type system with a distinct type for received timestamps.
Cargo features make it harder to ensure test coverage. Unless including a capability is very costly (e.g. in compile time or code size) it should always be compiled.
This seems like a major flaw in the draft. Maybe work with the author/propose a revision to address this gap?
A provided method that delegates to the existing one by default would work well. We may want to make a breaking release by the time this is all settled anyway, but we can combine them back down then if desired. |
Hi quinn maintainers, this issue contains a proposal and details to implement packet receive timestamps1.
Motivation
We would like to implement the GCC2 congestion control algorithm, commonly used for WebRTC, as a controller implementation in quinn. The GCC algorithm is both loss and delay based and require measurements in order to provide an estimated bitrate for the network link. ACK's currently provide enough information to the loss based controller because a loss can be measured by any skips in packet numbers, but the current design of ACK's lacks adequete information for the loss based controller. In the WebRTC domain, GCC is commonly used with TWCC3 an RTP/RTCP extension that provides feedback on the arrival time and sequence number of RTP packets. The Receive Timestamp draft1 is a QUIC extension that's analogous to TWCC.
The draft also contains a motivation section. Meta also seems to have their own implementation thats based off of this draft and presented45 their work to the IETF 118 meeting in Nov of 2023.
Implementation
This section contains the components and their implementation details needed to implement this feature. It would be useful to read through the proposal first to familiarize oneself with how it works.
Update TransportParmeters
max_receive_timestamps_per_ack
receive_timestamp_exponent
Question
The transport parameter values are not determined. How should we handle this? An option is to use a sufficiently large value that we believe there shouldn’t be a collision in a reasonable time if the official values are incremented sequentially.
Update AckFrame
Update the AckFrame to include the timestamp information.
Timestamp type chosen to be
Bytes
because it's handled similiar to theadditional
field, where an iterator is used to decode those bytes. In theadditional
case, the iterator returns an acknowledged packet number, in thetimestamp
case, the iterator returns the packet number and receiver receive time for that packet.Another option is to create a separate struct dedicated for Ack with timestamps so we wouldn't have to handle the Option on timestamps.
Encode and decode to/from wire format
The structure of the ACK_RECEIVE_TIMESTAMP is below
For encoding, I plan to implement an
Ack::encode_timestamps
helper function to encapsulate all the timestamp encoding logic in one place.For the decoding design, I plan to implement an
AckTimestampDecoder
struct that analogous to theAckIter
iterator. TheAckTimestampDecoder
is a struct that implements the iterator trait that allows the caller to get the packet number and timestamp, in decreasing order. The main motivation of this design is to keep this iterator consistent withAckIter
.Below is pseudocode for the proposed behavior.
Question Similar to the TransportParameter codes, the ACK_RECEIVE_TIMESTAMP Type is not specified. We would need a placeholder of some value in the interim.
Implement
ReceivedTimestamps
data structureReceivedTimestamps
is aVector
of monotonically increasing packet numbers that's used to store the packet number and time of a received packet.The draft proposal includes a section that states that out of order packets can be ignored.
If an incoming packet has a lower packet number than the packet number of the last value on the vector, it means that the incoming packet is out of order and the time it was received can be ignored.
Converting between timestamps to/from wire format to std::time objects
The absolute timestamp is not directly encoded into the ACK frame, but rather the delta is encoded and compared to a basis. This design helps optimize for space, reducing the need to use 64-bits to encode the NTP time, or the middle 32-bits of NTP, which is what other protocols do.
For the first timestamp delta of the first timestamp range, the received timestamp can be determined by adding the delta to the timestamp basis, which was negotiated via the transport parameters. Subsequent received timestamps can be computed by taking the difference between the last calculated received timestamp and the delta value.
For the GCC system that leverages the timestamps data, that system is only interested in the delta between the timestamps, rather than the absolute value of the timestamp. This use case seems like the exact problem
time::Instant
was designed to solve. Therefore, whenever time type is needed for the implementation, thetime::Instant
type is used. A downside of usingtime::Instant
is that it does not print into a human readable time, but that should be a reasonable trade-off anyways because the timestamp delta is based off of an arbritrary basis anyways.When a connection is created, a new
time::Instant
will be created and used effectively as theInstant
for thereceive_timestamp_basis
value. Both will never change for the lifetime of the connection, and all calculations done on them will be based on deltas. For calculations, we will use thetime::Instant::duration_since
method ortime::Instant + time::Duration
.The pseudocode below describes how the conversion from Instant to the time delta wire format.
Include time::Instant when adding received packets to PendingAcks
Within
Connection::on_packet_authenticated
andspace.pending_acks.insert_one
we can add the packet to the ReceivedTimestamp object. This would mean that theReceivedTimestamp
would live on thePacketSpace
struct.It makes sense to put the ReceivedTimestamps object on the PacketSpace because ACK’s are grouped by the packet space.
Receiving and handling Ack Frames with timestamps.
When the peer sends an ACK frame with timestamps, we do the following:
SentPacket
object.Some pseudocode:
Investigate current behavior of
quinn_proto::congestion::Controller
I believe that the current implementation of the
congestion::Controller
may contain a bug that would lead to some incorrect calculations, but is likely ignored by the controller implementations. The bug exists on any method on the controller that surfaces any packet number information. Packet numbers can be reused within separate packet number spaces and based on the existing implementation, it's possible for the controller to see the same packet number more than once. A simple example is when packet number 0 of spaceHandshake
is sent, and then packet number 0 of spaceData
. Theon_sent
method will be called twice with packet number 0.What we want is for the congestion control measurements to be unified across the spaces and not segmented by space.
A fix would be to implement a solution that provides the congestion controller a packet number counter that spans all packet spaces.
I validated my expectation by
println
'ing the packet number and spaces here.Extend
quinn_proto::congestion::Controller
to surface timestamp infoThe main consideration for adding a new method instead of modifying existing ones is to prevent a breaking a change on the public API.
on_acknowledgement
: called for each acknowledgement packet received, similiar to the API foron_ack
but includes thereceived
field.[Feature]
or notThe implementation above could be executed without the need of a feature flag. What are your thoughts on that? Should we use the rust
[features]
to prevent this code from getting compiled if its not enabled?Interactions with Ack with ECN (
0x03
)Based on some local testing I did between 2 quinn endpoints, it appears that all of the ACK frames that are sent between the two are type
0x03
, the ACK frame with ECN data. Because the draft proposes the receiver timestamp as a completely separate type that does not encode any ECN data, communicating ECN data and receiver timestamps are mutually exclusive, you can choose one, but not both. In this implementation, if the receiver timestamps feature is enabled, sending an ACK packet with timestamp data will take precedence over sending the ACK frame with ECN data.Open Questions
congestion::Controller
implementation? Should the fix be to use a global packet number counter that unified across the packet spaces?Footnotes
https://www.ietf.org/archive/id/draft-smith-quic-receive-ts-00.html ↩ ↩2
https://datatracker.ietf.org/doc/html/draft-ietf-rmcat-gcc-02 ↩
https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01 ↩
https://datatracker.ietf.org/meeting/118/materials/slides-118-quic-ack-timestamps-00 ↩
https://youtu.be/qbAm_HfLv_c?feature=shared&t=6705 ↩
https://www.rfc-editor.org/rfc/rfc9002#section-4.1 ↩
The text was updated successfully, but these errors were encountered: