awesome-everything RU
↑ Back to the climb

Networking & Protocols

Loss detection and congestion control

Crux QUIC uses monotonically increasing packet numbers and pluggable congestion control — NewReno, CUBIC, BBR — eliminating Karn''''s algorithm ambiguity and enabling per-application CC upgrades.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at middle altitude — in the sky
◷ 12 min

TCP retransmits a dropped packet with the same sequence number. The ACK that comes back is ambiguous — did the server acknowledge the original send or the retransmit? QUIC gives every packet a unique number, and this single change makes loss detection unambiguous and congestion control pluggable.

Monotonic packet numbers

QUIC packet numbers are strictly increasing integers — a lost packet gets a new packet number on retransmit. Unlike TCP where retransmitted packets carry the same sequence number (creating ambiguity about which send the ACK acknowledges — Karn’s algorithm workaround), QUIC never reuses a packet number.

ACK ranges: QUIC ACK frames list ranges of acknowledged packet numbers:

ack_ranges: [1000..1005, 1008..1010]

Gap at 1006 and 1007 means those packets are not yet received. This is equivalent to TCP SACK but native to the protocol.

Loss declaration happens when:

  1. A later packet is acknowledged and a reorder window has expired (typically 1.5× max RTT after the out-of-order packet was sent).
  2. Or the PTO (Probe Timeout) fires — analogous to TCP’s RTO but computed per packet-number space.

The monotonic model is simpler than TCP’s but requires retransmitted frames to be re-serialized with new packet numbers — a minor CPU cost.

Packet number model: TCP vs QUIC
TCP (ambiguous)
Send seq=100 → LOST
Retransmit seq=100 → ACK 101
ACK: original or retransmit?
→ Karn’s algorithm required
QUIC (unambiguous)
Send pkt#100 → LOST
Retransmit as pkt#101 → ACK 101
ACK#101 = retransmit confirmed
→ No ambiguity, no workaround

Pluggable congestion control

Because QUIC lives in user space, congestion control is pluggable. RFC 9002 specifies NewReno as the baseline. Major implementations ship:

  • NewReno — slow start, additive increase, halve cwnd on loss.
  • CUBIC (quiche from Cloudflare, Linux kernel standard) — aggressive probing after loss with a cubic function.
  • BBR (Google, YouTube, Cloudflare edges) — model bottleneck bandwidth and minimum RTT; ignore random loss.
  • HyStart++ — exit slow start early by detecting queueing, used with CUBIC.

Applications can select the algorithm at connection open. A single QUIC library can support multiple algorithms. This agility is impossible with kernel TCP where changing congestion control requires recompiling or sysctl changes across millions of servers.

The trade-off: Flexibility makes benchmarking hard — comparing two QUIC stacks with different CC algorithms is not a fair comparison of QUIC itself.

Quiz

Why does QUIC avoid Karn's algorithm, and what design choice makes this possible?

Quiz

Why is pluggable congestion control in QUIC significant compared to kernel TCP?

Which RFC?

Which RFC defines QUIC's loss detection algorithm and congestion control baseline?

Why this works

Why not just use TCP’s congestion control in QUIC? TCP’s congestion control is implemented in the kernel’s socket layer, where it has direct access to packet timestamps, kernel buffers, and NIC offload. Reimplementing it in user space (as QUIC does) costs CPU — each packet must be processed by user code without hardware assistance. The benefit: user space can iterate faster, can access application-level metadata (e.g., video stream priority) to make smarter CC decisions, and can be updated with the application without touching the OS.

QUIC congestion control algorithms
RFC 9002 baseline
NewReno (slow start + AIMD)
Cloudflare quiche default
CUBIC (aggressive probing)
Google YouTube / GCP
BBR (bandwidth-RTT model)
User-space CC update cycle
weekly (vs monthly+ for kernel TCP)
Per-connection CC selection
at connection open
Recall before you leave
  1. 01
    What is Karn's algorithm and why does QUIC not need it?
  2. 02
    How does QUIC's ACK range mechanism work, and what TCP feature does it resemble?
  3. 03
    What is PTO in QUIC and how does it differ from TCP's RTO?
Recap

TCP’s sequence number reuse on retransmit creates Karn’s algorithm ambiguity — you cannot measure RTT from a retransmitted ACK. QUIC eliminates this by assigning every send a new, strictly increasing packet number. ACK frames carry ranges so gaps in acknowledged numbers reveal lost packets exactly, equivalent to TCP SACK but always active. Loss is declared via a reorder window or PTO (computed per packet-number space). Because QUIC runs in user space, congestion control is pluggable: RFC 9002 specifies NewReno as the baseline, but CUBIC and BBR ship in major libraries and can be selected per-connection. Applications on CDN edges can update their CC algorithm weekly without touching the kernel.

Connected lessons
appears again in162
Continue the climb ↑0-RTT resumption and packet encryption
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.