Repository navigation
perf(moq-net): one allocation per long path join and per lite request track name - #7500
Conversation
Quest-Claim: fd635513aa843e5e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review.
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: a7a6f79
No actionable bugs found. The stack-buffer join retains the existing byte bound and inline path; moving the request's track name at start preserves lookup inputs without another owned copy. Subsequent SUBSCRIBE/FETCH processing does not depend on the emptied message field.
Direction: these are appropriately scoped ownership optimizations. Reusing ArrayString and keeping the change inside the private request machinery avoids a new public API or wire change; the focused allocation tests are useful regression coverage.
Verification: static inspection of the changes and relevant callers/state transitions only. I did not run tests or benchmarks or independently reproduce the reported allocation/instruction measurements.
(Written by OpenAI)
There was a problem hiding this comment.
ℹ️ No critical issues — one minor, log-only suggestion below.
Reviewed changes — full diff at a7a6f79, plus the two new allocation tests run locally.
Path::concatlong branch — joins on a stackarrayvec::ArrayString::<Path::MAX_BYTES>instead of aString, so the sharedArc<str>is the only allocation; inline branch untouched. Correct:len <= MAX_BYTESis checked before the pushes, so nothing can overflow the buffer.- Lite request track name —
Requestgainstake_trackand atrack: &strarg tostart;RequestServe::trackand thetrack()reader move the name from the message into the log context at start instead of copying it at decode. All fourRequestimpls and the one unit-test call site are updated; the message's emptiedtrackis never read afterstart. announce_allocs.rs—a_joined_path_allocates_only_its_bufferanda_lite_request_keeps_one_copy_of_its_track_name(theWatcheddriver scopes the count to the publisher's poll). Both pass;moq-netlib tests compile.- Quest bookkeeping —
lite-request-allocs.mddeleted and its README bullet removed; no dangling references.
ℹ️ RequestServe::track loses the name when a graceful request FINs mid-resolve
track() reads the name from the message only while the state is Resolve, and from self.track (populated by take_track at start) otherwise. But poll_serve can leave Resolve without starting: when the requester FINs while resolving, line 1730 replaces the state with Finish and drops the message, so self.track stays empty. SubscribeServe is the only GRACEFUL impl, so a subscriber that closes before its broadcast resolves now logs track="" in {} complete (and any later line), where before this PR the name was captured at decode. No behavior beyond logging: track() is the sole reader of self.track.
Technical details
# Preserve the track name across the FIN-during-resolve path
## Affected sites
- `rs/moq-net/src/lite/publisher.rs:1667-1671` — `track()` returns `&self.track` for any non-`Resolve` state.
- `rs/moq-net/src/lite/publisher.rs:1730` — `self.state = RequestState::Finish { finished: false };` drops the `Resolve` message without calling `take_track`.
## Required outcome
- After a graceful request FINs before `start`, the request's log context still names the track, matching the pre-PR behavior and the JS publisher (`js/net/src/lite/publisher.ts:1510` keeps logging `msg.track`).
## Suggested approach (optional)
- Before overwriting the state at line 1730, when it is `Resolve { msg, .. }`, set `self.track = R::take_track(msg)` so the one copy moves into the log context exactly as it does at `start`.deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
…olve A graceful request whose requester FINs before the broadcast resolves left `Resolve` without calling `start`, dropping the message and its name, so the `complete` log printed `track=""`. Move the name into the log context there too. Also port the new allocation test to `#[kio::test]`, since main moved moq-clock into kio::clock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Fixed Pullfrog's log-only finding in 5844c41: when a graceful request FINs while still resolving, (Written by Claude Opus 5.5) |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 5844c41
No new actionable bugs found. Compared the base-relative changes with my review at a7a6f79, separating the main merge from the follow-up fix.
Fixed: Pullfrog's FIN-during-resolve logging finding. In rs/moq-net/src/lite/publisher.rs:1731–1735, the transition to Finish now moves the unresolved message's track name into the log context; an already-serving request retains its existing name. The allocation test also correctly uses the current kio test harness.
Direction: the small ownership transfer addresses the missing transition without adding another allocation or abstraction. The original scoped optimization remains sound in the changed areas.
Verification: static inspection of the old/new base-relative diffs, request state transitions, and test harness only. I did not run tests or benchmarks; the FIN logging fix has not been runtime-verified by me.
(Written by OpenAI)
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the commits since the prior pullfrog review at a7a6f79.
- Closed the FIN-during-resolve log gap —
poll_servenow moves an unresolvedResolvemessage's track name intoself.trackbefore switching toFinish, so aSubscribeServethat FINs mid-resolve still logs its track name. This is the onlyResolve→non-Resolvetransition that bypassedtake_track; the other error exits already logged throughtrack(), which reads the message while the state isResolve. - Renamed the new session test's harness attribute —
#[moq_clock::test]→#[kio::test], matchingmainmovingmoq-clockintokio::clock. - Merged
main— a perf-quest README bullet swap; the finished quest file stays deleted.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
|
codex / Codex land fixer note:
(written by codex / Codex) |
|
codex / Codex land fixer: + "reason": "Healthy unchanged; passed quest check; bun run --cwd js/net build; bun run --cwd js/net test (2656 passed). Batch flaky test: a peer close drains the overtaken group over moq-lite-02.",
+ "comment": "This PR changes only quest documents. The document check and full JavaScript net build and test suite passed. The batch failure in `a peer close drains the overtaken group over moq-lite-02` did not recur. No changes were made."
+ },
+ {
+ "number": 7553,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed bun run --cwd js/net build; bun run --cwd js/net check; bun run --cwd js/net test (2661 passed); quest check. Batch flaky test: a peer close drains the overtaken group over moq-lite-02.",
+ "comment": "The expanded IETF join tests and full JavaScript net suite passed, along with the build, type checks, and quest document check. The batch failure was in an unchanged lite connection test, `a peer close drains the overtaken group over moq-lite-02`, and did not recur. No changes were made."
+ },
+ {
+ "number": 7509,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed cargo build --locked -p moq-net --benches; cargo nextest run --locked -p moq-net -p moq-net-tests --tests (3247 passed); bun run --cwd js/net build; bun run --cwd js/net test (2656 passed); quest check. Batch flaky test: a peer close drains the overtaken group over moq-lite-02.",
+ "comment": "The Rust benchmark build and all 3247 Nextest tests passed, along with the full JavaScript net build and test suite and quest document check. This PR does not change the JavaScript implementation or test implicated in the batch failure, `a peer close drains the overtaken group over moq-lite-02`, which passed locally. No changes were made."
+ }
+]
exec
/bin/bash -lc "git switch base; python3 -c 'import json, pathlib, subprocess; p=pathlib.Path(\"quest-land-result.json\"); r=json.loads(p.read_text()); expected={7500:\"5844c41152ec1f013f73557432b817f5c206f1c0\",7572:\"5217954b816bb0a4d801c4793fe0235d08fe58cb\",7553:\"378a07b7d0c390b6044c0802e57e1c80e7f56e56\",7509:\"94a1d2be089a2738e8f7c412921d1833a87cf334\"}; assert len(r)==4 and {e[\"number\"] for e in r}==set(expected); allowed={\"number\",\"action\",\"reason\",\"comment\"}; assert all(e[\"action\"]==\"requeue\" and set(e)<=allowed and len(e.get(\"comment\",\"\"))<=4000 for e in r); assert all(subprocess.check_output([\"git\",\"rev-parse\",f\"pr-{n}\"], text=True).strip()==sha for n,sha in expected.items()); print(\"Result validated; all four PR heads unchanged.\")'; git status --short" in /work
succeeded in 650ms:
Switched to branch 'base'
Result validated; all four PR heads unchanged.
?? quest-land-result.json
diff --git a/quest-land-result.json b/quest-land-result.json
new file mode 100644
index 0000000000000000000000000000000000000000..228e22e02dd4ef7d6543404427e4b7d4d3bbd649
--- /dev/null
+++ b/quest-land-result.json
@@ -0,0 +1,26 @@
+[
+ {
+ "number": 7500,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed cargo build --locked -p moq-net -p moq-net-tests; cargo nextest run --locked -p moq-net -p moq-net-tests --tests (3248 passed); bun run --cwd js/net build; bun run --cwd js/net test (2656 passed on rerun); quest check. Flaky tests: a peer close drains the overtaken group over moq-lite-02 (batch log), a version without a boundary syncs once the request is out (first local run).",
+ "comment": "The batch failed at js/net/src/lite/connection.test.ts:394 in `a peer close drains the overtaken group over moq-lite-02`. This PR changes Rust code and quest documents, not that JavaScript test or its implementation. The full net suite passed unchanged on rerun. The first local run instead failed the unchanged test `a version without a boundary syncs once the request is out`; both tests merit flake follow-up. Rust validation passed all 3248 tests under CI's Nextest runner. An initial plain cargo test run encountered a global tracing subscriber collision in `setup::tests::credentials_stay_out_of_traces`; Nextest's process isolation passed it. No changes were made."
+ },
+ {
+ "number": 7572,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed quest check; bun run --cwd js/net build; bun run --cwd js/net test (2656 passed). Batch flaky test: a peer close drains the overtaken group over moq-lite-02.",
+ "comment": "This PR changes only quest documents. The document check and full JavaScript net build and test suite passed. The batch failure in `a peer close drains the overtaken group over moq-lite-02` did not recur. No changes were made."
+ },
+ {
+ "number": 7553,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed bun run --cwd js/net build; bun run --cwd js/net check; bun run --cwd js/net test (2661 passed); quest check. Batch flaky test: a peer close drains the overtaken group over moq-lite-02.",
+ "comment": "The expanded IETF join tests and full JavaScript net suite passed, along with the build, type checks, and quest document check. The batch failure was in an unchanged lite connection test, `a peer close drains the overtaken group over moq-lite-02`, and did not recur. No changes were made."
+ },
+ {
+ "number": 7509,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed cargo build --locked -p moq-net --benches; cargo nextest run --locked -p moq-net -p moq-net-tests --tests (3247 passed); bun run --cwd js/net build; bun run --cwd js/net test (2656 passed); quest check. Batch flaky test: a peer close drains the overtaken group over moq-lite-02.",
+ "comment": "The Rust benchmark build and all 3247 Nextest tests passed, along with the full JavaScript net build and test suite and quest document check. This PR does not change the JavaScript implementation or test implicated in the batch failure, `a peer close drains the overtaken group over moq-lite-02`, which passed locally. No changes were made."
+ }
+]
codex
Wrote [quest-land-result.json](/work/quest-land-result.json): all four PRs requeued unchanged after passing validation.
No PR was blamed. The logged test failure appears flaky; details and commands are recorded in the result. All PR heads remain unchanged, and the result file is uncommitted.
diff --git a/quest-land-result.json b/quest-land-result.json
new file mode 100644
index 0000000000000000000000000000000000000000..228e22e02dd4ef7d6543404427e4b7d4d3bbd649
--- /dev/null
+++ b/quest-land-result.json
@@ -0,0 +1,26 @@
+[
+ {
+ "number": 7500,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed cargo build --locked -p moq-net -p moq-net-tests; cargo nextest run --locked -p moq-net -p moq-net-tests --tests (3248 passed); bun run --cwd js/net build; bun run --cwd js/net test (2656 passed on rerun); quest check. Flaky tests: a peer close drains the overtaken group over moq-lite-02 (batch log), a version without a boundary syncs once the request is out (first local run).",
+ "comment": "The batch failed at js/net/src/lite/connection.test.ts:394 in `a peer close drains the overtaken group over moq-lite-02`. This PR changes Rust code and quest documents, not that JavaScript test or its implementation. The full net suite passed unchanged on rerun. The first local run instead failed the unchanged test `a version without a boundary syncs once the request is out`; both tests merit flake follow-up. Rust validation passed all 3248 tests under CI's Nextest runner. An initial plain cargo test run encountered a global tracing subscriber collision in `setup::tests::credentials_stay_out_of_traces`; Nextest's process isolation passed it. No changes were made."
+ },
+ {
+ "number": 7572,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed quest check; bun run --cwd js/net build; bun run --cwd js/net test (2656 passed). Batch flaky test: a peer close drains the overtaken group over moq-lite-02.",
+ "comment": "This PR changes only quest documents. The document check and full JavaScript net build and test suite passed. The batch failure in `a peer close drains the overtaken group over moq-lite-02` did not recur. No changes were made."
+ },
+ {
+ "number": 7553,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed bun run --cwd js/net build; bun run --cwd js/net check; bun run --cwd js/net test (2661 passed); quest check. Batch flaky test: a peer close drains the overtaken group over moq-lite-02.",
+ "comment": "The expanded IETF join tests and full JavaScript net suite passed, along with the build, type checks, and quest document check. The batch failure was in an unchanged lite connection test, `a peer close drains the overtaken group over moq-lite-02`, and did not recur. No changes were made."
+ },
+ {
+ "number": 7509,
+ "action": "requeue",
+ "reason": "Healthy unchanged; passed cargo build --locked -p moq-net --benches; cargo nextest run --locked -p moq-net -p moq-net-tests --tests (3247 passed); bun run --cwd js/net build; bun run --cwd js/net test (2656 passed); quest check. Batch flaky test: a peer close drains the overtaken group over moq-lite-02.",
+ "comment": "The Rust benchmark build and all 3247 Nextest tests passed, along with the full JavaScript net build and test suite and quest document check. This PR does not change the JavaScript implementation or test implicated in the batch failure, `a peer close drains the overtaken group over moq-lite-02`, which passed locally. No changes were made."
+ }
+]
tokens used
79,565
Wrote [quest-land-result.json](/work/quest-land-result.json): all four PRs requeued unchanged after passing validation.
No PR was blamed. The logged test failure appears flaky; details and commands are recorded in the result. All PR heads remain unchanged, and the result file is uncommitted.
fixer result written
(written by codex / Codex) |

Finishes quest/m2/perf/lite-request-allocs.md, the two follow-ups from moq-dev/moq#6952.
Problem
Path::concat(soPath::joinfor any result over 22 bytes) built aString, then copied it into the path'sArc<str>: two allocations where one is needed.RequestServe::trackfor its logs.Approach
concatjoins a long path in anarrayvec::ArrayStringon the stack (uninitialized, likeietf::namespace::from_tuple), so the sharedArc<str>is the only allocation. The inline branch (22 bytes or less) is unchanged.RequestServeno longer copies the name. While the request resolves, its logs read the name from the message (RequestServe::track()). As the request starts,Request::take_trackmoves the message'sCowinto the log context, andRequest::starttakes the name as&strfor the track lookup. Nothing reads the message's track afterstart. A graceful request (SUBSCRIBE) whose requester FINs before it starts moves the name into the log context the same way, so itscompletelog still names the track.moq-net-tests/tests/announce_allocs.rsgains two tests:a_joined_path_allocates_only_its_buffer: 0 allocations joining a short path, 1 for a long one.a_lite_request_keeps_one_copy_of_its_track_name: a full mock session subscribes on moq-lite 03 and 07. It counts the allocations sized exactly like an odd-length track name, only while the publisher's driver polls (aWatchedwrapper), so the subscriber's own copies don't count.Measurements
All deterministic: allocation counts, and user-space instruction counts pinned to one E-core. Before is
mainat 2d13685.announce_allocs(the new tests, run against both):Path::join, 10 / 47 byte resultcargo bench -p moq-net --features fuzz --bench path(allocations per op; rows that changed):path/join/medium(26 bytes)path/join/longdecode/announce_lite07/mediumdecode/announce_lite07/longorigin/request_rooted_str/longThe other 28 rows are unchanged.
Instructions per join (
perf stat -e cpu_atom/instructions/u, 10M joins, net of an empty loop, using a standalone copy ofReprand bothconcats):Alternatives and choices
Repr::copy_boundedwas smaller code, but cost about 42 more instructions per inline join (122 vs 80). Only the shared branch uses theArrayString.RequestState::Resolveholds it, so thestartedlog'srequest = ?msgwould printtrack: "". Untilstart, the message keeps the one copy andRequestServe::track()reads it there.Request::starttakes a fourth argument (track: &str). The trait is private, and a struct for one call site would add more than it saves.support::harness::connect_mock, so it can wrap the server's driver inWatched. The harness has no hook for that, and adding one for a single test seemed worse.just bench BASErun. The host is shared and loaded, so wall-clock relay benches would be noise. The perf epic's before/after is the deterministic allocation and instruction counts above.Impact
Path::concatispub(crate), andRequestis private tolite::publisher.Checks
On
0b2bec95e5471624feb40401da0f53863164f8df, after mergingmainat62ccb69bac845563dc798a27b4665056e9e56a56:just ci origin/mainRust gate passed 9,477 tests, with 109 skipped, plus clippy, formatting, docs and dependency checks. Both allocation tests, malformed complete-frame and SUBSCRIBE-update tests, and path length-boundary tests passed.backend::quic_client_close_drains_finished_trackpassed on its first test run. moq-dev/moq#7634 remains separate and untouched; this run does not prove that shared race is fixed.cargo bench --locked -p moq-net --features fuzz --bench pathpassed: joins allocate 0 for short paths and 1 for medium/long paths; lite-07 announce decoding uses 3.60 / 5.01 allocations for medium/long paths.Reprandconcatinto an optimized standalone harness, with 10M joins pinned to CPU 0 andcpu_core/instructions/u, subtracting an empty loop. Base / current instructions per join: 70 / 71 (short), 59 / 59 (22-byte inline boundary), 340 / 211 (long). This isolates join CPU cost from host scheduling noise.quest check --base origin/mainandgit diff --check: passed.Merge from main
Iteration on 2026-11-02 merged
mainat 62ccb69 without textual conflicts. The one-allocation join, single request-name ownership, FIN logging fix, and borrowed wire decoding are preserved. Public API and wire formats remain unchanged.No textual conflicts. One semantic conflict: moq-dev/moq#7027 moved
moq-clockintokio::clock, so the new session test now uses#[kio::test]instead of#[moq_clock::test].Review
5844c41152ec1f013f73557432b817f5c206f1c0. Codex (quest review), gpt-6.1-sol, medium effort, session01a19806-fabb-75f1-8c4b-60ed4ee58f93, round 1: no findings.0b2bec95e5471624feb40401da0f53863164f8df. The only new commit is a conflict-free merge fromorigin/main, so Quest's review exemption carries the existing clean review forward. No implementation changes or new review round.Follow-ups
SubscriberDriver::pollandTrackServe::prepare_establish. The new test's backtraces showed both. This is already covered by Small hot-path waste.(Written by GPT-6.1 Sol)