Repository navigation
ACK_FREQUENCY: max_ack_delay ratchets up by the timer resolution on every exchange, and PTO keeps using the handshake max_ack_delay #6334
Description
Describe the bug
Two related behaviours in the ACK frequency extension, read from source at a01333c (v2.6.1) and still present on main at 9262a4a.
1. max_ack_delay ratchets upward by one timer tick per ACK_FREQUENCY exchange.
QuicConnGetAckDelayreturnsSettings.MaxAckDelayMs + MsQuicLib.TimerResolutionMswhen delayed ACKs are on (connection.h:1469 at a01333c; 1470 on main). On WindowsTimerResolutionMsis 16 (15 + 1, library.c:501).- The ACK_FREQUENCY frame's Requested Max Ack Delay is that value:
Frame.RequestedMaxAckDelay = MS_TO_US(QuicConnGetAckDelay(Connection))(send.c:923; 924 on main). - The receiver overwrites its own
Settings.MaxAckDelayMswith it (connection.c:5383-5389; 5398-5403 on main). - Either endpoint sends ACK_FREQUENCY when it is scheduling-limited and its packet-tolerance hint changes (send.c:1514,
QuicConnUpdatePeerPacketToleranceat connection.c:6398).
So when both endpoints send the frame, each one adds a tick to the other's current value: 25 → 41 → 57 → 73 ms, and so on. The value only ever rises. What an endpoint configured is not what its peer ends up using, and the result depends on how many exchanges happened.
2. PTO uses the peer's handshake max_ack_delay, never the value this endpoint requested.
QuicLossDetectionComputeProbeTimeout adds Connection->PeerTransportParams.MaxAckDelay (loss_detection.c:242, same on main). That is the transport parameter from the handshake, and ACK_FREQUENCY never updates it. After the endpoint requests a larger max_ack_delay, the peer may legitimately delay ACKs longer than the PTO allows for. draft-ietf-quic-ack-frequency-14, Section 7, says a sender can use the requested value for PTO, and MUST use the greater of the current and in-flight values while the frame is unacknowledged.
Combined: on a connection that has ratcheted to 57 ms here and 73 ms at the peer, this endpoint's PTO still allows for 41 ms.
Observed
Between two Windows machines running MsQuic 2.6.1 (a01333c, OpenSSL TLS), neither side sets MaxAckDelayMs. QUIC_PARAM_CONN_SETTINGS read on each end at the start and end of 79 streams (a value holds for its connection, which carries several streams) gave the following pairs (sender / receiver of the video datagrams):
| Sender | Receiver | Streams |
|---|---|---|
| 25 | 41 | 66 |
| 41 | 57 | 3 |
| 57 | 73 | 10 |
The receiver always read the sender's value + 16. Values read at the start and end of each stream were equal.
Impact
We could not measure a cost in our data. Across those 79 streams, the rate of datagrams declared lost and then acknowledged (QUIC_DATAGRAM_SEND_ACKNOWLEDGED_SPURIOUS) was 3.7e-3 at 25/41 and 3.3e-3 at 57/73. There was an excess in the 60-90 ms window, where the mismatch would act, but two streams from one run carried all of it. This was a small sample at our rates, and we are not claiming a cost. We are reporting the behaviour because it doesn't follow the draft, and because an application cannot choose its ACK timing: MaxAckDelayMs is overwritten by the peer's frames, and there is no setting for the packet tolerance (related: #2823).
Expected
- Requested Max Ack Delay reflects what the sender wants the peer to use (for example its configured
MaxAckDelayMs), without adding its own timer allowance on every hop. - PTO uses the requested max_ack_delay (the maximum of the current value and any in flight) after an ACK_FREQUENCY frame is sent, per draft Section 7.
Also noted
The receive path rejects RequestedMaxAckDelay < TimerResolution as a PROTOCOL_VIOLATION (connection.c:5363; 5377 on main) before the == 0 branch that follows, so that branch cannot be reached. #6212 / #6213 fixed the transport-parameter side of MaxAckDelayMs = 0. We haven't checked whether a sender with MaxAckDelayMs = 0 can still emit a Requested Max Ack Delay of 0.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity