Skip to content

quic: the send-retention limits are QuicOptions, set when building the reactor - #275

Merged
MDA2AV merged 1 commit into
mainfrom
feat/quic-send-retention-options
Oct 4, 2026
Merged

MDA2AV merged 1 commit into
mainfrom
feat/quic-send-retention-options

Conversation

@MDA2AV

@MDA2AV MDA2AV commented Oct 4, 2026

Copy link
Copy Markdown
Owner

The two QUIC send-retention limits are now set where the reactor is built, in QuicOptions, instead of on QuicEngine:

  • SendRetentionBytes (default 16 MiB, floored at 256 KiB as before): unacknowledged response bytes a connection holds before CanQueueSend turns false and a streamed response waits for acks.
  • SendRetentionCeilingBytes (default 32 MiB): past this the connection is closed - the backstop for a producer that ignores the wait. It was hard-wired at twice the high-water; it is its own knob now, never below SendRetentionBytes.

Each connection takes both from its reactor when it is accepted or opened, the way ReadTimeoutMs already reaches it; a client-only reactor (no QuicOptions) gets the defaults.

Breaking: QuicEngine's maxSendRetentionBytes parameter is gone - a caller passing it moves the value to QuicOptions.SendRetentionBytes.

The 13 QUIC Playground samples set both in their QuicOptions; docs/how-ioxide-does-h3.html and the generated panes follow.

Why it came up: a buffered HTTP/3 response is handed over whole, so 32 concurrent 1 MiB responses on one connection cross the 32 MiB ceiling and the server closes every such connection (16 of 16 under h2load -m 32; with one stream per connection the same responses work). The ceiling can be raised now; making the buffered path respect CanQueueSend is the real fix, and a separate change.

Tests

  • quic: a connection pushes back at QuicOptions.SendRetentionBytes (new): with a 256 KiB high-water, 300 KiB queued turns CanQueueSend false and the connection stays up.
  • quic: the send-retention backstop tells the peer before it drops the connection: now sets its 512 KiB ceiling through QuicOptions.
  • Both fail when the connection ignores the reactor's values and uses the old 16/32 MiB defaults, so they prove the values arrive.
  • TestServer.StartDatagram takes an optional quicOptions hook.

All suites pass: E2E 232, Unit 60, Chaos 47, Http 44, Tls 151 (the 7 kTLS tests skip without sudo), File 4.

Bench

h2load (--alpn-list=h3, 16 conns x 32 streams), interleaved 5 rounds with a second build of main as the control; median of the per-round ratio against main:

sample this PR control
Http3/Nghttp3Buffered -0.4% -0.6%
Http3/ManagedBuffered +0.3% -1.9%

…e reactor

SendRetentionBytes (the high-water, default 16 MiB, floored at 256 KiB) and
SendRetentionCeilingBytes (the backstop, default 32 MiB - hard-wired at twice the
high-water until now) move from the QuicEngine constructor to QuicOptions, and each
connection takes them from its reactor. The Playground QUIC samples and the docs set both.
@MDA2AV
MDA2AV merged commit 5693a64 into main Oct 4, 2026
1 check passed
@MDA2AV
MDA2AV deleted the feat/quic-send-retention-options branch October 5, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant