feat(net): mark the end of the initial announce set over moq-transport - #2826
feat(net): mark the end of the initial announce set over moq-transport#2826kixelated wants to merge 1 commit into
Conversation
moq-transport marks nothing between the namespaces a publisher already had and the ones that showed up a moment later, so a subscriber can see that a namespace is present but never that it is absent. moq-lite has had that boundary since lite-05 (ANNOUNCE_OK.Active Count). Adds the MoQ Namespace Count extension: a NAMESPACE_COUNT Setup Option asking for a NAMESPACE_COUNT parameter on the REQUEST_OK that opens a namespace response set. Unknown Message Parameters are fatal in moq-transport, so the parameter is negotiated rather than sent unasked, and there is no registry for control message types, so an explicit end-of-set message was not an option. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 29 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (28)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 909eb52b80
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let initial = self.initial_namespaces(&mut announced, &prefix, &mut ns); | ||
| let namespace_count = declared.namespace_count.then_some(initial.len() as u64); |
There was a problem hiding this comment.
Avoid reporting an empty set when solicitation is disabled
When a peer negotiates Namespace Count but omits or disables the independent Solicit option, the origin above is replaced with origin.empty(), so this reports Some(0) even if the publisher has live namespaces being sent through unsolicited PUBLISH_NAMESPACE requests. A subscriber following the new draft may therefore conclude that the publisher does not have a namespace while its advertisement is still in flight. The mirrored JS implementation has the same behavior; either the count must be omitted in this mode or the extension must require solicitation before treating the response as a complete snapshot.
Useful? React with 👍 / 👎.
| let (session, mut driver) = Session::new(session, v, None, start.driver); | ||
| driver.wait_ready(|waiter| start.connecting.poll_ready(waiter)).await; |
There was a problem hiding this comment.
Bound the namespace-count readiness wait
For direct moq_net::Client callers, a peer can return NAMESPACE_COUNT = N, send fewer than N entries, and leave the response stream and session open; this new wait_ready then prevents connect() from ever returning. The timeout mentioned in the extension comments exists only in the higher-level moq_native wrapper, while this public generic client has no deadline, contrary to the new draft's requirement that subscribers not wait indefinitely.
Useful? React with 👍 / 👎.
Closes #2789.
Summary
SUBSCRIBE_NAMESPACEis answered withREQUEST_OKand then aNAMESPACEper match, all the same message on the same stream. A subscriber can see that a namespace is present but never that it is absent, which is whatorigin::Consumer::request_broadcastneeds before falling back to a dynamic handler orUnroutable. moq-lite has had that boundary since lite-05 (ANNOUNCE_OK.Active Count).drafts/draft-lcurley-moq-namespace-count.md: aNAMESPACE_COUNTSetup Option asking for aNAMESPACE_COUNTMessage Parameter on eachSUBSCRIBE_NAMESPACE_OK. That manyNAMESPACEmessages are the initial set; everything after is a live update.0is a real answer (the prefix is empty), absent means the peer doesn't implement it.HOP_PATHviaRELAY_HOPS, and we only send it to a peer that asked.REQUEST_UPDATE_OKthat accepts a change of Track Namespace Prefix (§10.9.2), since that starts a fresh set on the same stream and needs its own boundary. That clause is spec-only today: we open one stream per prefix and never send or accept a prefix update.NAMESPACE: draft-14/15 answer withPUBLISH_NAMESPACErequests on streams of their own, so there is nothing on the response stream to count, and draft-14'sSUBSCRIBE_NAMESPACE_OKcarries no parameters at all. We neither ask nor answer there.Implementation
rs/moq-net/src/ietf/namespace_count.rs: the option/parameter codepoints and the SETUP round trip, shaped likeietf::solicit.run_subscribe_namespace_streamnow waits for the peer's SETUP before the response (it decides whether the parameter goes on it), drains the origin synchronously into the initial set, and writes theREQUEST_OKplus its countedNAMESPACEmessages as one buffer. Selection runs once, during the drain, so the count and the messages can't disagree; a route that moves after that is a live update. Split-horizon-filtered namespaces are not counted, since they are never sent.NAMESPACEmessages have arrived (immediately for0or for a peer that reports none). Reflected announcements we drop locally still count: the peer doesn't know we dropped them.Connectingmoved fromlite/toconnecting.rsandietf::startnow returns it, soconnect()blocks on the initial announce set over moq-transport exactly as it already does over moq-lite. The wait is bounded by the existing connect deadline (moq_native::ClientConfig::timeout, 30s default), which the draft requires.js/netmirrors the wire: declares the option, sends the count as a publisher, parses it as a subscriber. It does not gate anything on it yet, because js/net has no connect-gate to hang it on (its lite subscriber ignoresAnnounceOk.activefor the same reason). Wire-compatible in both directions today; a JS-side boundary is a follow-up.Public API changes
None.
ietfandliteare private modules;ietf::SessionStart,connecting::{Connecting, ConnectingProducer}, andietf::peer::Peer::namespace_countare allpub(crate)or crate-internal. The only exported change is a doc-comment update onorigin::Consumer::announced_broadcast.Cross-package sync
rs/moq-netwire →js/net(done),drafts/(the new draft),doc/(a bullet indoc/concept/standard/interop.md). Nohang,moq-ffi, or CLI surface is touched.Test plan
just check/just test.Some(0)vsNone(ietf::request), SETUP negotiation per version (ietf::namespace_count), publisher response counting (initial set, empty set, peer that didn't ask, split-horizon-filtered namespace excluded), subscriber connect gating (counted set completes, short set never completes, empty/absent complete immediately).js/net/src/ietf/namespace_count.test.ts(SETUP negotiation per version,0nvs absent on the response).nix develop --command just drafts checkparses the new draft.Not run: the cross-language interop matrix (
just test smoke-full).(Written by Opus 5)