fix(gui): stop detail-pane render storm that froze traffic exploration - #5
Open
plutack wants to merge 2 commits into
Open
fix(gui): stop detail-pane render storm that froze traffic exploration#5plutack wants to merge 2 commits into
plutack wants to merge 2 commits into
Conversation
Exploring captures (rapidly clicking rows in the All-traffic view) froze the UI, and large bodies eventually crashed the WebKit web process (JS heap exhaustion; confirmed by a user-session WebKitWeb- Process SIGABRT coredump). Four compounding causes, all fixed: - The 2s poll loop always installed fresh array identities into state, re-rendering the entire tree — including any open detail pane — even when nothing changed. Loaders now bail out via functional setState when a row fingerprint is unchanged. - BodyViewer re-ran prettyBody (JSON.parse + stringify), regex syntax highlighting, and a full innerHTML swap on every render, because dangerouslySetInnerHTML got a new object identity each time. All transforms are now memoized, pretty-print/highlight are skipped above 1 MiB (with an explanatory notice), and the innerHTML prop identity is stable. - Every capture detail embedded an ExportSnippet that auto-fired a snippet conversion on each selection change — one goja JS VM + httpsnippet eval per click. Conversion is now debounced 250ms and the snippet <pre> no longer re-assigns innerHTML per poll. - Rapid row clicks each fetched the full body payload and rendered every response on arrival. Detail fetches are now deduplicated per row while in flight, and superseded responses are dropped. GetCapture also stopped shipping bodies twice (UTF-8 + base64); base64 alone halves the response the webview has to parse. Validated live on Linux/webkit2gtk: a 5 MiB body that killed the app outright now opens fine, and 12 rapid clicks across 1 MiB-body rows settle cleanly (transient RSS spike, full GC recovery, UI responsive throughout). Idle polling no longer rebuilds the detail DOM: RSS is flat between real data changes.
Memoization removed the repeated cost but the FIRST render of a multi-megabyte body still blocked the webview for 0.5-2s: the whole body was base64-decoded per byte, escaped with three regex passes, and materialized as DOM text. Measured on a 4.8 MB JSON body, one open plus a few row switches cost the shipped v0.2.0 ~7.1s of webview CPU and ~1 GB of RSS growth; the same interaction now costs ~0.5s with flat RSS. The viewer now decodes and renders at most DISPLAY_LIMIT (256 KiB) up front, with a notice and a 'Show entire body' escalation. Copy, Save, and Image still operate on the full payload, decoded on demand. escapeHTML is a single pass, and the base64 prefix is cut on a 4-character boundary so the slice always decodes cleanly.
Owner
Author
|
The memoization killed the repeated cost, but opening a multi-MB body still blocked the webview for 0.5–2s per open — the entire body was base64-decoded byte-by-byte, escaped with three regex passes, and materialized as DOM text. Rapid row switching = that cost per distinct row.
Measured, shipped v0.2.0 vs this branch — same 4.8 MB JSON body, same click program (open big row + rapid row switching)
The 1 GB RSS balloon is the sluggishness (GC/swap thrash) and the direct precursor to the WebKit heap-exhaustion crashes. |
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
Exploring captures — rapidly clicking rows in the All-traffic view (e.g. capture 5 → 6 → 5 → 5) — froze the GUI, and large bodies eventually crashed the WebKit web process (JS heap exhaustion; confirmed by a user-session
WebKitWebProcessSIGABRT coredump with 71 MB core).Root causes (four, compounding)
prettyBody(JSON.parse + stringify), regex JSON highlighting, and a fullinnerHTMLswap ran on every render (thedangerouslySetInnerHTMLobject identity was new each time). For an N-byte body this rebuilt ~4× N of strings/DOM every 2 seconds.Fixes
setStatewhen a row fingerprint is unchanged (webhooks by project/seq, captures by id:status, sessions by id:count:ended) — Preact skips the re-render entirely.innerHTMLprop identity is stable.<pre>uses a memoized HTML object.GetCaptureships base64 only.Validation (live, Linux/webkit2gtk)