Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
f41a4fd
chore: claim quest/m1/quic/receive-memory (lease design)
kixelated Oct 17, 2026
57aef67
wip(net,quic): receive leases with transport-side eviction
kixelated Oct 17, 2026
e904a75
wip(tokio,uring): native receive leases, MIN_RECEIVE_WINDOW at config…
kixelated Oct 17, 2026
4624082
test(quic): receive credit recovery over tokio and uring, with a Bun …
kixelated Oct 17, 2026
8dc4025
test(tokio): partial headers and control requests give way to media
kixelated Oct 17, 2026
87c0e29
feat(quic): stream windows follow the connection window; document leases
kixelated Oct 17, 2026
0189b5d
Merge remote-tracking branch 'origin/main' into quest/m1/quic/receive…
kixelated Oct 17, 2026
d2ba58e
style: just fix
kixelated Oct 17, 2026
ab28f92
fix(net): clippy on the lease test, frame bench reader, and adapter recv
kixelated Oct 17, 2026
670710b
docs(quic): finalize names the lease instead of a private helper
kixelated Oct 17, 2026
d92fb2a
Merge remote-tracking branch 'origin/main' into quest/m1/quic/receive…
kixelated Oct 17, 2026
e8c3c12
fix(uring): Local forwards receive leases
kixelated Oct 17, 2026
81f51eb
fix(net): Buffered leases whole chunks, so memory frames skip the copy
kixelated Oct 17, 2026
ec22321
Merge branch 'main' of github.com:moq-dev/moq into quest/m1/quic/rece…
kixelated Oct 17, 2026
9c65ddc
fix(quic): a frame past the window is evicted, not refused
kixelated Oct 17, 2026
fa6a87a
style: just fix
kixelated Oct 17, 2026
483a847
Merge branch 'main' of github.com:moq-dev/moq into quest/m1/quic/rece…
kixelated Oct 17, 2026
bed0268
fix(tokio): inline the receive window floor into validate_windows
kixelated Oct 17, 2026
0bd5bdc
test(relay): both runtimes resolve the 128 MiB connection default
kixelated Oct 17, 2026
7b33e65
docs(quic): say why the connection window defaults to 128 MiB
kixelated Oct 17, 2026
c25c062
Merge branch 'main' of github.com:moq-dev/moq into quest/m1/quic/rece…
kixelated Oct 17, 2026
1b350ef
Merge remote-tracking branch 'origin/main' into quest/m1/quic/receive…
kixelated Oct 20, 2026
4b79302
fix(quic): issue stream credit when an eviction frees a stream
kixelated Oct 20, 2026
043eff5
fix(test): offer the Bun receive-credit sender a versions list
kixelated Oct 20, 2026
97d9381
feat(quic): count evictions, and state when none can happen
kixelated Oct 20, 2026
642e9dd
fix(net): rank received groups in their subscription's order
kixelated Oct 20, 2026
fd93fcf
test(tokio): run receive credit through a bufferbloated path
kixelated Oct 20, 2026
fda34cb
quest: complete the receive-memory quest; add migration notes
kixelated Oct 20, 2026
d5f47a1
Merge remote-tracking branch 'origin/main' into quest/m1/quic/receive…
kixelated Oct 20, 2026
530ddeb
fix(quic): satisfy clippy in the lease fuzz check
kixelated Oct 20, 2026
ca3b2ef
docs(relay): say which stream a full receive window cancels
kixelated Oct 20, 2026
bb98753
Merge remote-tracking branch 'origin/main' into iter/receive-memory-l…
kixelated Oct 20, 2026
29df3fd
fix(quic): keep evicting while freed credit only pays shrink debt
kixelated Oct 20, 2026
9bb364f
fix(quic): rank eviction candidates once per stuck pass
kixelated Oct 20, 2026
5d94ba1
fix(quic): keep taken bytes' credit until an evicted reader stops
kixelated Oct 21, 2026
34c8c03
Merge remote-tracking branch 'origin/main' into iterate/receive-memor…
kixelated Oct 21, 2026
9193920
fix(quic): rerun the stuck pass when a release only pays shrink debt
kixelated Oct 21, 2026
6bd0315
fix(quic): copy waiting leases out of their receive buffers
kixelated Oct 21, 2026
2da2698
refactor(net)!: read leases as a Buf, released explicitly
kixelated Oct 21, 2026
5ba074c
quest(m2): one receive budget per worker, shared with the cache
kixelated Oct 21, 2026
77b6c3b
perf(quic): keep the stuck-window check linear, and bench leases
kixelated Oct 21, 2026
c98e07f
bench(tokio): sweep the receive window, pipeline the sender, read the…
kixelated Oct 21, 2026
9549b37
bench(tokio): bound each track's pipeline in bytes, with a live-edge …
kixelated Oct 21, 2026
e1bac05
style: rustfmt
kixelated Oct 21, 2026
8e4d59a
Merge remote-tracking branch 'origin/main' into iterate/receive-memor…
kixelated Oct 21, 2026
6e3d976
test(tokio): finish the eviction test's writers
kixelated Oct 21, 2026
ee8482c
Merge remote-tracking branch 'origin/main' into iterate/receive-memor…
kixelated Oct 21, 2026
c66bdf5
fix(quic): rerun the stuck pass after every arrival, resets included
kixelated Oct 21, 2026
8e7816f
Merge remote-tracking branch 'origin/main' into iterate/receive-memor…
kixelated Oct 21, 2026
82bd6fc
docs(tokio): point the driver overview at poll_lease
kixelated Oct 21, 2026
8b45473
Merge remote-tracking branch 'origin/main' into iterate/receive-memor…
kixelated Oct 22, 2026
f9e65cd
fix(quic): rerun the stuck pass when a finished lease is stopped
kixelated Oct 22, 2026
142ffcc
fix(quic): hold eviction while a reader waits behind a gap
kixelated Oct 22, 2026
d8edc99
Merge remote-tracking branch 'origin/main' into merge-5878
kixelated Oct 22, 2026
0c299dc
Merge remote-tracking branch 'origin/main' into merge-5878
kixelated Oct 22, 2026
d774d74
Merge remote-tracking branch 'origin/main' into quest/m1/quic/receive…
kixelated Oct 22, 2026
ea3118e
fix(quic): count shrink debt toward peak spent credit
kixelated Oct 22, 2026
7b5bb39
fix(quic): saturate the peak spent credit sum
kixelated Oct 22, 2026
9b4ff7a
Merge origin/main into quest/m1/quic/receive-memory-lease
kixelated Oct 22, 2026
3a2e6d9
Merge remote-tracking branch 'origin/main' into quest/m1/quic/receive…
kixelated Oct 22, 2026
993e7d4
Merge origin/main into quest/m1/quic/receive-memory-lease
kixelated Oct 22, 2026
18b43ea
Merge remote-tracking branch 'origin/main' into quest/m1/quic/receive…
kixelated Oct 22, 2026
d6e3b56
test(net): hand out scripted reads a packet at a time
kixelated Oct 22, 2026
3600731
fix(net): free a reader's cross-chunk copy when it parks
kixelated Oct 22, 2026
c2013d9
Merge remote-tracking branch 'origin/main' into quest/m1/quic/receive…
kixelated Oct 22, 2026
8f2f880
fix(quic): skip the stuck check for arrivals that move no credit
kixelated Oct 22, 2026
efd0977
Merge remote-tracking branch 'origin/main' into pr5878
kixelated Oct 22, 2026
0acedb3
Merge remote-tracking branch 'origin/main' into HEAD
kixelated Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ test-group = "io-uring"
filter = "package(moq-ffi) & test(=bench::benchmark_ffi)"
slow-timeout = { period = "60s" }

# The receive-window benchmark sweeps a grid twice, once through a bottleneck
# whose rate bounds how fast a run can go, so it outlasts the hang budget.
[[profile.default.overrides]]
filter = "package(moq-tokio) & test(=receive_credit::benchmark_receive_windows)"
slow-timeout = { period = "60s", terminate-after = 5 }

[test-groups]
io-uring = { max-threads = 4 }

Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/interop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ jobs:
run: nix develop --command just test max-age
shell: bash -leo pipefail {0}

- name: Receive credit recovery
id: receive-credit
if: ${{ !cancelled() && steps.setup.outcome == 'success' }}
run: nix develop --command just test receive-credit
shell: bash -leo pipefail {0}
env:
# Fail rather than skip the io_uring arm if the runner's kernel lacks it.
MOQ_STRICT: 1

Comment thread
coderabbitai[bot] marked this conversation as resolved.
- name: Harness regressions
id: harness
if: ${{ !cancelled() && steps.setup.outcome == 'success' }}
Expand Down Expand Up @@ -196,6 +205,7 @@ jobs:
"Rust cache": "${{ steps.rust-cache.outcome }}",
"Playwright Chromium": "${{ steps.setup.outcome }}",
"Optional publisher retention": "${{ steps.max-age.outcome }}",
"Receive credit recovery": "${{ steps.receive-credit.outcome }}",
"Harness regressions": "${{ steps.harness.outcome }}",
"Subscription termination interop": "${{ steps.bare-fin.outcome }}",
"P2P data-channel transport": "${{ steps.p2p.outcome }}",
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ jobs:
- "test/max-age/**"
- "test/portrait/**"
- "rs/moq-tokio/tests/integration/max_age.rs"
- "test/receive-credit/**"
- "rs/moq-tokio/tests/integration/receive_credit.rs"
- "rs/moq-uring/tests/integration/receive_credit.rs"
# The interop job also depends on the recipe that invokes it and the
# workspace membership that links the JS clients to source.
- "test/justfile"
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 11 additions & 5 deletions doc/bin/relay/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ idle_timeout = "10s" # Drop a connection after this long with no
keep_alive = "3s" # Ping interval; "0s" disables it. iroh still pings at least every 5s. Default.
gso = true # UDP segmentation offload. iroh cannot turn it off.
mtu_discovery = false # Path MTU discovery. Default.
receive_window = 67108864 # Whole-connection window, in bytes. Default (64 MiB).
receive_window = 134217728 # Whole-connection window, in bytes. Default (128 MiB).
stream_receive_window = 4194304 # Per-stream window, in bytes. Default (4 MiB).
send_window = 16777216 # Unacknowledged outgoing data, in bytes. Default (16 MiB).
qlog = "/var/log/moq/qlog" # Existing directory. Needs the `qlog` build feature.
Expand All @@ -96,10 +96,16 @@ QUIC uses the smaller of the two endpoints' values, so this also bounds the
other end. Keep `keep_alive` under a third of it.

Raise the receive windows when a fat, long path idles below the link rate. The
64 MiB `receive_window` default carries several Gbps at a 100 ms RTT and bounds
how much unread data one peer can make the relay buffer. Keep
`stream_receive_window` well under it so one slow group cannot starve the
connection.
128 MiB `receive_window` default carries several Gbps at a 100 ms RTT and bounds
how much unread data and incomplete messages one peer can make the relay
buffer. It has to fit the largest message, just over 64 MiB, so a smaller
value, here or in `[peer.quic]`, is refused at startup. When incomplete
messages fill the window, the relay cancels the least urgent stream: the
lowest subscription priority, then the group its subscription would deliver
last. Over iroh the window bounds only
unread data, and over WebSocket or TCP nothing bounds incomplete messages.
`stream_receive_window` only paces one stream: its credit returns as bytes are
read, whether or not their message is complete.

## \[session] and \[peer]

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"test/interop/clients/js",
"test/interop/clients/js-native",
"test/drain",
"test/receive-credit",
"test/video",
"test/p2p",
"test/qmux",
Expand Down
5 changes: 2 additions & 3 deletions quest/m1/quic/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ MoQ never negotiates. One core serves the tokio backend, the thread-per-core
`moq-uring` backend, and qmux; iroh stays on upstream noq. BBR correctness,
reliable stream resets (#5690), hierarchical send groups (#5711, #5770), one
`Limits` type for relay peers (#5871), and endpoint sharding (#5688) are on
`main`. What remains is bounded incomplete-message memory and clean exits
for connection-blocked writers; per-broadcast
`main`, and receive leases bound incomplete-message memory (#5878). What
remains is clean exits for connection-blocked writers; per-broadcast
fairness gives the crate-private send domains (#5872) their consumer in
[Broadcast fairness](/quest/m2/broadcast-fairness.md).
qmux already runs on the shared stream state machine, outside this epic.
Expand Down Expand Up @@ -71,7 +71,6 @@ cache limits remain separate; bitrate pacing composes with this memory cap.

## Required

- [Bound message assembly](/quest/m1/quic/receive-memory.md) - complete headers and frames return credit, and receiver priority cancellation restores progress
- [Connection-blocked writers end cleanly](/quest/m1/quic/connection-blocked.md) - an ended stream leaves the blocked-writer list, and STOP_SENDING surfaces under exhausted connection credit

## Related
Expand Down
119 changes: 0 additions & 119 deletions quest/m1/quic/receive-memory.md

This file was deleted.

1 change: 0 additions & 1 deletion quest/m1/release-audit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ child records whether its behavior is new on main or inherited from release.

- [JS subscription ranges](/quest/m1/subscribe-ranges/js.md) - range lists and frame offsets build on the bounds fixed here
- [OBS video source](/quest/m2/obs-moq-video/source.md) - reproduced inherited timing and EOF defects remain with the decoder replacement owner
- [Receive memory](/quest/m1/quic/receive-memory.md) - incomplete reliable input remains a separate connection-credit concern
- [QPACK dependency fix](/quest/m0/web-transport-proto-0.6.3.md) - decoded header expansion is already tracked
- [Compressed tracks](/quest/m1/compress/README.md) - mid-group widening and application migration have existing owners
- [Archive consumer](/quest/m1/archive/consumer.md) - current producer and CLI reader still need the new-format roundtrip
Expand Down
1 change: 0 additions & 1 deletion quest/m1/release-audit/datagram-memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,4 @@ with the maintainer before implementing it.

## Related

- [Reliable receive memory](/quest/m1/quic/receive-memory.md) - parser-held incomplete streams, not completed datagram queues
- [Compressed tracks](/quest/m1/compress/README.md) - shared compression behavior and fallback
1 change: 0 additions & 1 deletion quest/m1/release-audit/webtransport-streams.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,4 @@ if affected.

## Related

- [Receive memory](/quest/m1/quic/receive-memory.md) - classification must preserve connection-credit ownership
- [QPACK dependency fix](/quest/m0/web-transport-proto-0.6.3.md) - separate decoded-header expansion problem
9 changes: 3 additions & 6 deletions quest/m1/rs-request-credit.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@ Propose the error variant's name in the PR. Public API: one new `Error`
variant, and possibly a `transport::poll` session method. Wire: none.

Decided 2026-10-20 (maintainer): the `transport::poll` trait breaks land in
order, [receive memory](/quest/m1/quic/receive-memory.md) first, then this
quest, then the send domain in [broadcast fairness](/quest/m2/broadcast-fairness.md), so
order, this quest on the receive-lease shape from
[moq-dev/moq#5878](https://github.com/moq-dev/moq/pull/5878), then the send
domain in [broadcast fairness](/quest/m2/broadcast-fairness.md), so
implementers rebase on one shape at a time.

## Required

- [Bound message assembly](/quest/m1/quic/receive-memory.md) - lands its `transport::poll` change first; this quest builds its non-waiting open on that shape
1 change: 1 addition & 0 deletions quest/m2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ ranked just below deterministic TS legs, which it requires.
- [Ordered owed wait](/quest/m2/ordered-owed-wait.md) - an ordered reader waits within max_delay for a missing group only while the track still owes it
- [lite-05/06 gap judgement](/quest/m2/lite-legacy-gap.md) - a resumed copy on lite-05/06 never reads a descending track's newest group as its start
- [Unbounded groups](/quest/m2/unbounded-groups/README.md) - groups and frames lose their size caps; the cache sheds parts of a group under pressure and refills the holes in place
- [Receive pool](/quest/m2/receive-pool.md) - one byte budget per worker covers every connection's receive credit and the cache, billing each frame once
- [Rust-backed @moq/net](/quest/m2/rs2ts/README.md) - measure browser baselines, complete WASM bindings, and adopt Rust behind @moq/net before the later JS-only stage
- [Front parking](/quest/m2/origin-front-parks.md) - an unroutable request waits on a front instead of re-asking on every route-table move
- [Route wakes](/quest/m2/route-wakes.md) - a route change wakes only the fronts it can move, so pool churn stops scaling with served paths
Expand Down
3 changes: 1 addition & 2 deletions quest/m2/broadcast-fairness.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Direction carried over from 2026-09-19, not re-confirmed:
- The draft would say a relay MAY scope priority to the broadcast on a
relay-to-relay connection; no carrier is needed on the wire.
- Decided 2026-10-20 (maintainer): the `transport::poll` trait breaks land in
order, [receive memory](/quest/m1/quic/receive-memory.md), then
order, receive leases ([moq-dev/moq#5878](https://github.com/moq-dev/moq/pull/5878)), then
[Rust request credit](/quest/m1/rs-request-credit.md), then the send domain.

Receive ranks, folded in from
Expand Down Expand Up @@ -103,4 +103,3 @@ update moves an already-ranked stream.
## Related

- [One parked group per subscription](https://github.com/moq-dev/moq/pull/6140) - the parked group each subscription keeps must still yield the next stream by broadcast
- [Bound incomplete messages](/quest/m1/quic/receive-memory.md) - the receive-side counterpart on the same moq-quic sessions
6 changes: 1 addition & 5 deletions quest/m2/rate-quic.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ backpressure the peer cannot ignore, rather than metering and closing.
flow-control limited (its congestion controller does not back off) and
queues until [the grant](/quest/m2/rate-grant.md) clamps its encoder.
Keep rate pacing separate from the connection memory allowance in
[message assembly](/quest/m1/quic/receive-memory.md). The memory allowance
message assembly (receive leases, [#5878](https://github.com/moq-dev/moq/pull/5878)). The memory allowance
must fit the largest supported encoded unit even when it takes many rate
intervals to receive. Limit each grant by both rate allowance and available
memory backing; do not wait for a whole-message credit release to grant
Expand Down Expand Up @@ -95,7 +95,3 @@ pacing alone does not trigger pressure cancellation.
Decided in the 2026-10-10 receive-memory interview: memory allowance and
rate pacing are independent bounds. This replaces the earlier assumption
that a one-second rate window can also be the complete assembly allowance.

## Required

- [Bound message assembly](/quest/m1/quic/receive-memory.md) - separate retained memory from paced outstanding credit
Loading
Loading