Skip to content

NetworkLogSource: lazy/streaming load for sessions exceeding MAX_DISPLAY_LINES #126

@handstandsam

Description

@handstandsam

Context

NetworkLogSource.parse currently caps rendered events at MAX_DISPLAY_LINES = 5_000, keeping only the most recent N via takeLast. For typical web sessions (a few hundred to low thousands of events) this is invisible; for a 30-minute test on a chatty SPA hitting tens of thousands of requests, the panel only shows the last 5_000 and the rest are silently dropped from the view.

The cap was kept deliberately for the initial wiring; this issue tracks lifting it.

Why a hard cap exists

Two reasons:

  1. LazyColumn stays responsive only up to a point; rendering 50k JSON-derived rows starts to stutter on scrub.
  2. parse() materializes the full filtered list before slicing (lineSequence().filter { … }.toList().takeLast(N)) — the upper bound on memory cost is set by the cap, not by file size.

Strategies to evaluate

A) Streaming parse with a ring buffer.
Replace lineSequence().filter{}.toList().takeLast(N) with a streaming pass that maintains an ArrayDeque<ParsedLogLine> of size N. O(1) memory per line during parse instead of O(file size). Doesn't change the cap, just stops doubling memory transiently. Cheapest win; ~5 LOC.

B) Range-based windowed parse.
Parse only events whose timestampMs falls within the user's currently scrubbed window (plus a buffer on either side). Re-parse on scrub. Requires keeping the file content in memory but materializing only the visible slice. Cap effectively becomes "N events around the cursor" rather than "last N events ever." Best UX for long sessions.

C) Pre-built index file.
At capture-write time (or first-load time), build a sidecar index of (byteOffset, timestampMs) per event. Range queries become disk-seek + parse-only-visible-bytes. Rules out re-parsing the whole file on every scrub. More machinery; pairs naturally with future response-body capture work.

D) Two-pane: counter header + paginated detail.
The header always shows the true total ("12,432 events captured"). The list shows a window with paging controls. No data is hidden, but interaction shifts from "scroll forever" to "page through ranges." Low-effort, high-clarity.

Recommendation

Start with A as a free win (no API change, no behavior change for sessions under the cap), then evaluate B as a v2 that genuinely supports long sessions. C depends on broader on-disk schema changes. D is a UX call — nice to have alongside any of the above.

Estimated diff

A: ~10 LOC. B: ~80 LOC. C: ~200 LOC + on-disk schema. D: ~50 LOC of Compose.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions