perf(desktop): skip pre-send parent fetch on reply when thread root is cached#1467
Open
tlongwell-block wants to merge 1 commit into
Open
perf(desktop): skip pre-send parent fetch on reply when thread root is cached#1467tlongwell-block wants to merge 1 commit into
tlongwell-block wants to merge 1 commit into
Conversation
…s cached Every reply paid a full relay round trip before send: resolve_thread_ref fetched the parent event just to walk its NIP-10 e-tags for the thread root — data the frontend timeline cache almost always already has. send_channel_message grows an optional cached_root_event_id. The TS send mutation resolves the root from the cached timeline via a new resolveCachedReplyRoot that mirrors the Rust resolver exactly (last root marker wins, else last reply marker, else parent is root; same parent-kind allowlist including KIND_HUDDLE_STARTED=48100; self-referential markers collapse to the parent). Any cache miss or out-of-allowlist kind returns null and the Rust side falls back to the existing relay resolution — behavior unchanged. Correctness: the cached root is used only for tag construction; the relay's ingest-side ancestry validation (root tag must match thread ancestry) still rejects any stale/wrong root, so the fast path cannot produce an accepted-but-misthreaded event. A unit suite pins the TS mirror to the Rust resolver semantics, including the trap where resolveReplyRootId's parent-id fallback would mislabel a nested reply as a thread root. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Every reply pays a full relay round trip before the send even starts:
send_channel_messagecallsresolve_thread_ref, which fetches the parent event from the relay just to walk its NIP-10 e-tags for the thread root. The frontend timeline cache almost always already has that parent — the user just clicked "reply" on it.This is the reply-RTT fix from the perf trace (best perceived-latency-per-line in the desktop lane): reply send latency drops by one relay RTT in the common case.
Change
commands/messages.rs):send_channel_messagegrows an optionalcached_root_event_id. When present,thread_ref_from_cached_rootbuilds theThreadReflocally; when absent, the existingresolve_thread_refrelay path runs unchanged. Applies to both the forum-comment and default send arms.features/messages/lib/threading.ts): newresolveCachedReplyRoot(parentEventId, events)mirrors the Rust resolver exactly — lastrootmarker wins, else lastreplymarker, else the parent is the root; self-referential markers collapse to the parent;s.len() >= 4marker guard. It also gates on the same parent-kind allowlistresolve_thread_refqueries (9, 40002, 45001, 45003, 48100) — a cached parent of any other kind returnsnullso behavior can't diverge from the relay path (which would report "parent event not found").features/messages/hooks.ts): resolves the root from the timeline cache it already reads for optimistic tags, and passes it through. Only the send-mutation block is touched — per the lane partition with Wren (Optimize desktop channel live subscriptions #1466 owns the subscription/bridge regions of this file).shared/api/tauri.ts: parameter plumbing (+2 lines).Correctness
nullon cache miss, non-allowlisted kind, or missing markers ⇒ Rust falls back to the existing relay resolution. The fast path only engages when the answer is byte-identical to what the relay fetch would return.root tag does not match thread ancestry,handlers/ingest.rs) rejects any stale or wrong root — the fast path cannot produce an accepted-but-misthreaded event, only the same rejection a bad reply gets today.threading.resolveCachedReplyRoot.test.mjs) locks the TS mirror to the Rust semantics: marker precedence, last-wins tag walk, self-referential collapse, short/unmarked e-tag rejection, kind allowlist (including theKIND_HUDDLE_STARTED=48100kind — an earlier draft had 45021 here; the test now makes that drift red), and the trap whereresolveReplyRootId's parent-id fallback would silently mislabel a nested reply as a thread root (the cached resolver returnsnullinstead).Validation
On this head (branch cut from main
e42dae3f, which is an ancestor of HEAD — verifiedmerge-base --is-ancestorin the same shell as the push):pnpm --dir desktop test: 1503 passed / 0 failed (includes the 9 new resolver tests)pnpm --dir desktop typecheck: cleanpnpm check(biome + file sizes + px-text): exit 0cargo test(desktop src-tauri): all greencargo clippy --all-targets(desktop src-tauri): no new warnings in touched filesFile-size overrides for
messages.rs(+24) andtauri.ts(+2) bumped with rationale comments, per the existing queued-to-split convention.