QUIC Loss Detection and Congestion Control
Extracted elements (31)
Persistent congestion is detected using a time duration rather than consecutive PTO events, because application send patterns can prevent PTO timers from expiring even when no acknowledgments are received. A duration-based approach allows persistent congestion to be established independently of PTO expiration.
QUIC recommends a minimum congestion window of two packets rather than TCP's one. With a one-packet minimum, loss forces the sender to wait for a PTO to recover, and sending a single ack-eliciting packet increases the chance of additional latency from receiver-delayed acknowledgments.
QUIC uses a Probe Timeout (PTO) instead of TCP's RTO and TLP. Unlike TCP's RTO, QUIC does not collapse the congestion window on PTO expiration since a single tail loss does not indicate persistent congestion, avoiding the need for corrective mechanisms like F-RTO. The congestion window collapses only when persistent congestion is declared.
QUIC uses monotonically increasing packet numbers within each packet number space, directly encoding transmission order and eliminating TCP's retransmission ambiguity problem. This enables more accurate RTT measurements and allows spurious retransmissions to be trivially detected without complex inference of transmission ordering.
QUIC uses separate packet number spaces per encryption level (except 0-RTT and 1-RTT share a space) to prevent acknowledgments at one encryption level from spuriously triggering retransmissions at another. RTT measurement and congestion control are unified across all packet number spaces.
A client MUST set the PTO timer if it has not received an acknowledgment for any Handshake packets and the handshake is not confirmed, even with no packets in flight, to unblock the server constrained by its anti-amplification limit. On PTO fire it MUST send a Handshake packet if it has Handshake keys, otherwise an Initial packet padded to at least 1200 bytes.
A PTO timer expiration event MUST NOT cause prior unacknowledged packets to be marked as lost; it only triggers sending of probe packets. Loss detection from packet and time thresholds proceeds only when an acknowledgment is received.
After handshake confirmation, an endpoint MUST use the lesser of the ACK delay and the peer's max_ack_delay when adjusting RTT samples, and MUST NOT subtract the ACK delay if it would make the adjusted sample smaller than min_rtt, limiting underestimation from a misreporting peer.
An endpoint MUST NOT send a packet if it would cause bytes_in_flight to exceed the congestion window, except when sending on PTO timer expiration or when entering recovery.
An endpoint MUST NOT set its PTO timer for the Application Data packet number space until the handshake is confirmed, preventing retransmission of data before either peer has keys to process the packets or their acknowledgments.
An RTT sample MUST NOT be generated on receiving an ACK frame that does not newly acknowledge at least one ack-eliciting packet, to avoid incorporating arbitrarily large ACK Delay values from ACKs covering only non-ack-eliciting packets.
Endpoints SHOULD use an initial congestion window of ten times the maximum datagram size, limited to the larger of 14,720 bytes or twice the maximum datagram size, per RFC 6928 with the byte limit increased to account for UDP's 8-byte versus TCP's 20-byte overhead.
If a sender uses a different congestion controller than the NewReno algorithm specified in this document, the chosen controller MUST conform to the congestion control guidelines in Section 3.1 of RFC 8085.
Implementations SHOULD NOT use a packet reordering threshold (kPacketThreshold) less than 3, which is the RECOMMENDED initial value based on TCP loss detection best practices. Adaptive algorithms like RACK that increase the threshold after spurious losses are expected to be at least as useful in QUIC as in TCP.
In congestion avoidance, the increase to the congestion window MUST be limited to at most one maximum datagram size per congestion window acknowledged, implementing additive increase.
min_rtt MUST be set to latest_rtt on the first RTT sample and to min(min_rtt, latest_rtt) on all subsequent samples. Endpoints SHOULD reset min_rtt to the newest RTT sample after persistent congestion is established to avoid repeatedly declaring persistent congestion when the RTT increases.
On entering a recovery period, a sender MUST set ssthresh to half the congestion window at loss detection, and the congestion window MUST be set to the reduced ssthresh value before exiting recovery. A single packet MAY be sent immediately upon entering recovery to speed up loss recovery.
Probe packets MUST NOT be blocked by the congestion controller, but a sender MUST count them as additionally in flight since they add network load. Senders MUST either use pacing or limit bursts; SHOULD limit bursts to the initial congestion window.
The congestion window SHOULD NOT be increased during slow start or congestion avoidance when bytes_in_flight is smaller than the congestion window and sending is not pacing-limited, to avoid inflating the window during underutilization caused by application or flow control limits.
The time threshold for loss detection MUST be at least kGranularity (recommended 1 ms), computed as max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity) with recommended kTimeThreshold of 9/8. Using max(smoothed_rtt, latest_rtt) protects against both lower and higher-than-expected latest RTT samples.
When a PTO timer expires, a sender MUST send at least one ack-eliciting packet as a probe. All probe packets sent on a PTO MUST be ack-eliciting. The sender MAY send up to two full-sized datagrams to reduce the probability of consecutive PTO events from a single lost datagram.
When a PTO timer expires, the PTO backoff MUST be increased by doubling the PTO period. The backoff is reset when an acknowledgment is received, except a client does not reset the PTO backoff on receiving Initial packet acknowledgments until the server completes address validation.
When Initial or Handshake packet protection keys are discarded, the sender MUST discard all recovery state for those packets and MUST remove them from bytes in flight. The PTO and loss detection timers MUST be reset because discarding keys signals forward progress.
When persistent congestion is declared — all packets sent between two lost ack-eliciting packets over a duration exceeding (smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay) * kPersistentCongestionThreshold are unacknowledged — the congestion window MUST be reduced to kMinimumWindow (recommended 2 * max_datagram_size).
RTT estimation tracks three values per path: min_rtt (minimum observed over a period, unadjusted for ACK delay), smoothed_rtt (exponentially weighted moving average), and rttvar (mean deviation). These are initialized from kInitialRtt (recommended 333 ms) and updated on each RTT sample drawn from ACK frames.
smoothed_rtt and rttvar are updated as: smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt; rttvar = 3/4 * rttvar + 1/4 * |smoothed_rtt - adjusted_rtt|. adjusted_rtt subtracts the peer-reported ACK delay bounded by max_ack_delay (only enforced after handshake confirmation) provided the result is not less than min_rtt.
The PTO period is computed as: PTO = smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay. For Initial and Handshake packet number spaces max_ack_delay is 0. When ack-eliciting packets in multiple spaces are in flight, the timer MUST be set to the earlier of the Initial and Handshake values.
A receiver can misreport ECN-CE markings: suppressing them inflates the sender's rate potentially causing congestion, while reporting extra markings unnecessarily reduces it. Senders can detect suppression by occasionally marking packets ECN-CE and disabling ECN if those marks are not reflected in subsequent acknowledgments.
Loss detection and congestion control consume signals — delay, loss, and ECN markings — from unauthenticated entities. An attacker can reduce endpoints' sending rates by dropping packets, strategically altering path delay, or changing ECN codepoints in transit.
Packets carrying only ACK frames can be heuristically identified by their size, potentially revealing link characteristics or application behavior patterns. Endpoints can mitigate this by bundling acknowledgments with other frames or by using PADDING frames at a potential performance cost.
The NewReno congestion controller defines three states: Slow Start (window below ssthresh, grows by bytes acknowledged per ACK); Recovery (entered on loss or ECN-CE increase, window reduced to half then held until a packet sent during recovery is acknowledged); Congestion Avoidance (window at or above ssthresh outside recovery, AIMD growth). Persistent congestion or a new path resets to Slow Start.