Repository navigation
colibri 0.10.0: CUBIC, HyStart++ and h3 under loss
colibri 0.10.0 makes h3 hold up on paths with delay and loss. QUIC now runs CUBIC with HyStart++ in place of NewReno, carries up to 16 STREAM frames in a packet, and holds 100 h3 requests open. The server's router matches a path that ends in "/" with its own route, and h2 indexes a response's field lines in HPACK. It pins chapulin 1f3a99cb and stdx 5fd03aa, as 0.9.0 did.
Breaking changes
quic.constants.congestion_loss_reduction_divisoris gone, because CUBIC scales the window to 0.7 on a congestion event, not to half (decision 133).quic.constants.cubicandquic.constants.hystarthold the new constants.server.constants.quic_requests_maxis 100, where it was 32 (decision 130). A server's QUIC connection holds more streams, soserver.Endpointat its default capacity grows from 40,987,680 to 51,310,368 bytes, andquic.Connectionfrom 141,912 to 176,888 bytes. docs/performance.md lists each size.
What's new
- The congestion controller is CUBIC (RFC 9438, decision 133), in integer arithmetic, so one seed still replays on every host. Its first slow start is HyStart++ (RFC 9406, decision 135), which ends slow start when the round trip grows, before a loss does. spec/lean proves the cube root, the target's bounds and the HyStart++ stages, and two TLA+ models check the window and the stages in every order.
- A packet carries up to 16 STREAM frames (decision 131), so many small responses share one packet. h3-many takes 0.685 of 0.9.0's instructions a request.
- A server holds 100 h3 requests open and sends MAX_STREAMS each time a stream closes (decision 130). h3 body meters wait on data credit alone.
- A route whose pattern ends in "/" matches a path that ends in "/" (decision 122 as amended). A parameter does not match the empty last segment, and the wildcard does not match a path that ends in "/".
- h2 writes a response's field lines with incremental indexing in HPACK, so a repeated line is one byte (decision 132).
quic.data_credit_owedsays whether a connection waits on the peer's data credit, which the server's body meters read.
Fixes
- A server dropped every packet of a connection whose client started its first key update before the server had opened any of its 1-RTT packets, until the idle timeout. RFC 9001 §6.1 allows that update once the client confirms the handshake. quinn's client did it after a lost handshake datagram.
Speed
Each figure is colibri's p99 over h3 divided by its p99 over h2 with TLS, on the arm64 judge with tc netem on the loopback device (design §8 step 13i). Below 1, h3 answers the slowest requests sooner. The 0.9.0 tree's figure follows where it differs:
far(25 ms each way): 0.55 to 0.64 at 1,000 to 4,000 req/s.lossy(25 ms each way, 1% loss): 0.70 to 0.76, where it was 1.32 to 1.34 at 4,000 req/s. colibri's p99 over h3 is about 108 ms there, and quic-go's chi's about 107 ms.mobile(50 ms each way with jitter, 2% loss): 0.62 to 0.82 at 1,000 and 2,000 req/s, where it was 4.46 to 5.02 at 2,000. At 4,000 no h3 server reaches the rate: colibri reaches about 3,240 req/s with every request answered, and chi about 3,270.- h3-many takes 0.685 of the instructions a request it took before 16 STREAM frames shared a packet.
Checked
GitHub's main workflow passed on a23be1b2 (run 41012227601).