Proportional Rate Reduction (PRR)
obsoletes
- rfc-6937 — Proportional Rate Reduction for TCP
Extracted elements (24)
PRR bases transmission decisions on the volume of delivered data rather than the volume of lost data, unlike RFC 6675 where a single SACK-carrying ACK implying large missing data can cause a step discontinuity in the pipe estimator and trigger a large unclocked burst. This is the core motivation for PRR's self-clocking approach.
PRR-CRB satisfies the Strong Packet Conservation Bound: it never transmits more data than has been delivered to the receiver, ensuring a bottleneck queue maintains exactly constant length during recovery. However, this bound alone is less aggressive than RFC 6675 in burst-loss scenarios, motivating PRR-SSRB as a compromise that allows one extra segment per ACK when recovery is progressing well.
PRR is less sensitive to errors in the inflight estimator than RFC 6675. When inflight is misestimated (e.g., underestimated due to premature loss marking of reordered data), PRR's response differs by at most 1 segment per ACK between its two sub-algorithms, whereas RFC 6675 can send large bursts that cannot be retracted once the estimator corrects.
RecoverFS was changed to properly account for sequence ranges SACKed before entering fast recovery, correcting a defect in RFC 6937 where PRR would send too slowly after entering recovery following a large amount of pre-recovery SACKing (e.g., when the sender had previously detected high reordering).
Setting cwnd = ssthresh at the end of recovery (not the start, as in RFC 6675) is critical for robust performance: without it, application-limited senders and certain loss patterns could end fast recovery well below ssthresh, causing worse performance than RFC 6675.
The SafeACK heuristic was introduced after traffic policing research showed that both PRR-CRB and PRR-SSRB performed poorly under high-loss policer conditions. PRR-CRB was too timid and PRR-SSRB too aggressive, both causing prolonged recovery. SafeACK dynamically switches: use PRR-CRB by default, switch to PRR-SSRB only when ACKs show SND.UNA advancing without new losses.
PRR can be adapted for non-loss-based congestion control (e.g., ECN per RFC 3168) by using parts of the RFC 6675 loss recovery state machine to invoke PRR ACK processing for exactly one round trip. However, interactions with AQM and ECN approaches should be assessed per RFC 9743 guidance.
PRR is incrementally deployable and requires changes only at the data sender; no changes are required at receivers, routers, switches, or other network devices. PRR senders are fully compatible with existing receivers and networks.
PRR MAY be used with loss detection algorithms from Reno (RFC 5681), NewReno (RFC 6582), SACK (RFC 6675), FACK, and RACK-TLP (RFC 8985). PRR requires only that the loss detection algorithm communicate how many packets were marked lost and delivered per ACK, with no dependency on the loss detection details.
At the beginning of a congestion control response episode, a data sender using PRR MUST initialize PRR state variables: ssthresh (via CongCtrlAlg()), prr_delivered = 0, prr_out = 0, and RecoverFS computed from SND.NXT - SND.UNA adjusted for SACKed bytes.
Because of RACK-TLP's resilience to tail loss, reordering, and lost retransmissions, it is RECOMMENDED that PRR be implemented together with RACK-TLP loss recovery [RFC8985].
For non-SACK connections, senders MUST subtract min(RecoverFS, 1 SMSS per preceding duplicate ACK) from the inflight estimate, rather than subtracting SACKed bytes. The min() with RecoverFS protects against misbehaving receivers.
If prr_out is 0 and SndCnt is 0 upon entering recovery, SndCnt MUST be forced to SMSS to ensure a fast retransmit is sent, maintaining the ACK clock and avoiding potential RTO events.
It is RECOMMENDED that implementations use pacing to reduce the burstiness of data traffic, especially when cwnd is set to ssthresh at the end of recovery, which can permit a burst of back-to-back segments.
On completion of a PRR episode (end of fast recovery or start of a new congestion control response), cwnd MUST be set to ssthresh. This prevents cwnd from ending recovery well below ssthresh due to application-limited behavior.
PRR MAY be used with any congestion control algorithm that intends to make a multiplicative decrease in sending rate over approximately one RTT, where in-flight data is limited by cwnd and the target volume during reduction is a fixed ssthresh value. PRR is explicitly applicable to Reno and CUBIC.
SACK-enabled connections using RACK-TLP or other non-pipe loss detection MUST calculate inflight as SND.NXT - SND.UNA, minus SACKed bytes, minus bytes marked lost, plus bytes retransmitted since last marked lost.
Without SACK, PRR disallows incrementing DeliveredData when the total bytes delivered in a PRR episode would exceed RecoverFS, as a mitigation against misbehaving receivers that return extraneous duplicate ACKs to artificially inflate DeliveredData.
cwnd is updated after each ACK as cwnd = inflight + SndCnt. On every data transmission or retransmission, prr_out is incremented by the number of bytes sent.
DeliveredData is the sender's best estimate of bytes delivered since the previous ACK. With SACK it equals the change in SND.UNA plus the signed change in SACKed bytes. Without SACK, it is estimated as 1 SMSS per duplicate ACK, or the change in SND.UNA minus 1 SMSS per preceding duplicate ACK on a full/partial ACK.
Recovery Flight Size (RecoverFS) is the sender's estimate of bytes possibly delivered during a PRR episode. It is initialized as SND.NXT - SND.UNA, minus bytes already SACKed in scoreboard, plus newly SACKed bytes, plus newly cumulatively acknowledged bytes. It remains constant during a recovery episode.
SafeACK is a local boolean that is true only when the current ACK has cumulatively acknowledged new data and does not indicate further losses (e.g., does not trigger an RFC 6675 'rescue' retransmission). It controls whether PRR-SSRB or PRR-CRB is used when inflight is below ssthresh.
PRR does not change the risk profile for transport protocols. However, implementations that count segments instead of bytes must be cautious about ACK splitting attacks (Savage99), where a misbehaving receiver acknowledges partial segments to confuse the sender's congestion accounting.
When inflight > ssthresh, PRR uses Proportional Rate Reduction: SndCnt = DIV_ROUND_UP(prr_delivered * ssthresh, RecoverFS) - prr_out. When inflight <= ssthresh, PRR defaults to PRR-CRB: SndCnt = MAX(prr_delivered - prr_out, DeliveredData), then adds SMSS if SafeACK is true (PRR-SSRB), capped at MIN(ssthresh - inflight, SndCnt).