Skip to content

Coalesce queued preview renders latest-wins (bd-r8iqmg2f) - #970

Merged
shikokuchuo merged 9 commits into
mainfrom
feature/bd-r8iqmg2f-render-coalescing
Oct 30, 2026
Merged

shikokuchuo merged 9 commits into
mainfrom
feature/bd-r8iqmg2f-render-coalescing

Conversation

@shikokuchuo

@shikokuchuo shikokuchuo commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Summary

Phase 1 of the main-thread render-path plan (finding 2, bd-r8iqmg2f).

Before, a burst of N keystrokes ran N full preview renders. Now the render queue keeps only the newest waiting render per entry point, so a burst runs at most 2 renders: the one in flight plus the latest. The ReactPreview render entry also goes through the queue now — before, it skipped the queue entirely and burst renders could overlap on non-reentrant VFS state.

Superseded renders settle with a RenderSupersededError rejection that both preview components swallow (name-matched, so partial vi.mock('@quarto/preview-runtime') doubles cannot break the check). The lastContentRef stale-result guards stay as the second line of defense and now fire only for the in-flight render.

Measured

Node burst driver against the real WASM with a warm SASS cache; "before" is the same driver run at HEAD (reproduces the Phase 0 baseline):

Scale Burst Renders before Renders after Wall before Wall after
~99 KB 10 10 2 (8 superseded) 3026 ms 554 ms
~800 KB 5 5 2 (3 superseded) 12 367 ms 4476 ms

Envelope JSON.parse bytes per burst fell 106.9 to 21.4 MB at 1x.

Tests

  • renderQueue.test.ts: 7 queue-level tests; the Phase 0 no-coalescing pin flips to the new contract.
  • preview-runtime 88/88, hub-client test:ci 437 passed / 28 skipped, npm run build:all green, strict render of the plan file green. No snapshot changes.
  • A full PR review (2026-10-30) was done; its follow-up commits are on the branch.
  • Live-browser smoke done (2026-10-30): a Playwright drive of the dev server (vite dev + test hub + real WASM) typed a paced 10-keystroke burst on a ~300 KB document — ~5 renders ran, 6-7 superseded, preview converged to the last content, no RenderSupersededError leaked — and a print fired mid-burst serialized FIFO (popup contained the burst text). Two consecutive passes; recipe and observed output recorded in the plan.

Phase 1 of the 2026-10-24 main-thread render-path plan (finding 2). A
burst of N content updates previously executed N full render pipelines;
the serial render queue now coalesces waiting (not yet started) renders
latest-wins per entry point, so a burst executes at most the in-flight
render plus the latest queued one. renderPageInProjectWithAttribution
(the ReactPreview entry) was not queued at all before — burst renders
overlapped on non-reentrant VFS state; it now routes through the queue.

Superseded callers settle by rejecting with RenderSupersededError;
ReactPreview/Preview swallow it (name-matched so partial
vi.mock('@quarto/preview-runtime') doubles can't break the check). The
lastContentRef stale-result guards stay as the second line of defense
and now fire only for the in-flight render. First render after an idle
queue still starts immediately (no debounce).

Measured (Node driver, real WASM, warm SASS cache; before = same
driver at HEAD): a 10-keystroke burst at ~99 KB executes 2 renders
instead of 10 (8 superseded), wall 3026 -> 554 ms; at ~800 KB, 2
instead of 5, wall 12367 -> 4476 ms. Envelope JSON.parse bytes per
burst 106.9 -> 21.4 MB at 1x. Details in the plan.

Tests: renderQueue.test.ts flips the Phase 0 no-coalescing pin to the
new contract (5 tests). preview-runtime 86/86, hub-client test:ci
437 passed / 28 skipped, npm run build:all green. No snapshot changes.
@posit-snyk-bot

posit-snyk-bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

PR review of bd-r8iqmg2f: nothing imports the helper. The two preview
components deliberately inline the name check so partial
vi.mock('@quarto/preview-runtime') doubles — which would omit the
import just as they omit the class — cannot break it, so the helper's
only possible consumers cannot use it under their own constraint.
Removes the function from the package surface (index.ts re-exports
wasmRenderer wholesale).
PR review of bd-r8iqmg2f, simplification: the waiting-slots store was
an array plus a find() with the 'at most one per coalesce key'
invariant asserted only in a comment. A Map<key, slot> expresses it in
the data structure; insertion-ordered iteration is exactly the FIFO
the pump drains in, and supersede mutates the slot in place so it
keeps its position (the anti-starvation property). Behavior unchanged;
the existing characterization tests pin it.
…bd-gj6fk5hc)

PR review of bd-r8iqmg2f found renderPrintable still bypassed the
queue with a live caller (hub-client's openPrintableDocument): a print
fired while a preview render was in flight ran concurrently against
the non-reentrant WASM VFS state — the bug class bd-r8iqmg2f fixed for
the ReactPreview entry.

It now enqueues like the other render entries, but under a FRESH
coalesce key per call: prints serialize with everything else yet never
supersede, since each print is an explicit user action that must run.
renderQmd stays unqueued (exported but caller-less). Tests pin both
halves: a print fired mid-render waits its turn (never concurrent,
FIFO), and overlapping prints all execute.
Dev-server smoke (Playwright, vite dev + test hub + real WASM, since
deleted per the Phase 0 driver precedent): a paced 10-keystroke burst
on a ~300 KB document ran ~3 burst renders with 6-7 superseded
(two consecutive runs), the print fired mid-burst serialized FIFO and
its popup contained the burst text, the preview converged to the last
content, and no RenderSupersededError leaked. Recipe + gotchas
recorded in the plan.
@shikokuchuo
shikokuchuo merged commit 1ea01e4 into main Oct 30, 2026
15 checks passed
@shikokuchuo
shikokuchuo deleted the feature/bd-r8iqmg2f-render-coalescing branch October 30, 2026 14:07
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.

2 participants