Skip to content

perf(desktop): fix log ingest freeze and wrap log lines#683

Merged
skevetter merged 4 commits into
mainfrom
feat/desktop-log-perf
Jul 19, 2026
Merged

perf(desktop): fix log ingest freeze and wrap log lines#683
skevetter merged 4 commits into
mainfrom
feat/desktop-log-perf

Conversation

@skevetter

@skevetter skevetter commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

High-volume command output froze the main process. The bottleneck was the ingest path, not rendering:

  • Synchronous appendFileSync per line blocked the event loop. LogStore now uses buffered async write streams; closeLog flushes and closes on command exit (and is awaitable so reads see the full log).
  • One IPC message per line flooded the channel. A shared log sink coalesces lines into batched command-progress events (flush every 64 ms or 250 lines). CommandProgress gains an optional lines[] field; message is kept for provider-init and success detection.
  • Unbounded renderer buffer. The live buffer is now a capped ring buffer (5,000 lines, trimmed with hysteresis) in the detail page and create wizard. The full log stays on disk.

Display

LogTable drops manual fixed-height virtualization in favor of content-visibility windowing: rows render in normal flow so long lines soft-wrap, while offscreen rows are skipped by the browser. A hard row cap with a "N earlier lines hidden" notice keeps a huge saved log from creating excessive DOM nodes.

Verification

Drove a synthetic 30,000-line burst of long lines: the UI stayed responsive (tab-switching worked mid/post-flood) and lines wrapped correctly. svelte-check clean; 249 unit tests pass (updated the two log-store tests that read right after appending to await the async flush).

Addresses the logs performance/wrapping concern from #671.

Summary by CodeRabbit

  • New Features

    • Command output is now delivered in efficient batches for smoother live progress updates.
    • Long-running workspace logs are capped to keep the interface responsive.
    • Log viewers show the latest entries with a clear indicator when older lines are hidden.
  • Bug Fixes

    • Improved log writing and shutdown handling reduces missing or incomplete log output.
    • Workspace launch and lifecycle progress handling is more reliable.

The main process froze under high-volume command output. Root causes,
all on the ingest path rather than rendering:

- Synchronous appendFileSync per line blocked the event loop. Switch
  LogStore to buffered async write streams (closeLog flushes on exit).
- One IPC message per line flooded the channel. Coalesce lines into
  batched command-progress events (flush every 64ms or 250 lines) via a
  shared log sink; add an optional lines[] field to CommandProgress.
- The renderer log buffer grew unbounded. Cap it (ring buffer, 5000
  lines with hysteresis) in the detail page and create wizard.

Display: rewrite LogTable to drop manual virtualization in favor of
content-visibility windowing, so long lines soft-wrap while offscreen
rows stay cheap. Cap rendered rows with a 'lines hidden' notice so a
huge saved log can't create excessive DOM nodes.

Verified with a 30k-line burst: UI stays responsive and lines wrap.
@netlify

netlify Bot commented Jul 18, 2026

Copy link
Copy Markdown

Deploy Preview for images-devsy-sh canceled.

Name Link
🔨 Latest commit 91ccf6c
🔍 Latest deploy log https://app.netlify.com/projects/images-devsy-sh/deploys/6a5bb036ea78bb0008e8ed65

@netlify

netlify Bot commented Jul 18, 2026

Copy link
Copy Markdown

Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit 91ccf6c
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a5bb036bc1dbd000819efac

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@skevetter, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 34104100-87d7-4614-8ef1-f0c004652473

📥 Commits

Reviewing files that changed from the base of the PR and between c8a282e and 91ccf6c.

📒 Files selected for processing (7)
  • desktop/src/main/cli.ts
  • desktop/src/main/ipc.ts
  • desktop/src/main/log-store.ts
  • desktop/src/renderer/src/lib/components/log/LogTable.svelte
  • desktop/src/renderer/src/lib/components/workspace/WorkspaceWizard.svelte
  • desktop/src/renderer/src/lib/types/index.ts
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
📝 Walkthrough

Walkthrough

Workspace command output is batched through IPC and buffered file writes, while renderer buffers and log table rendering receive memory and DOM limits. Progress payloads now support multiple lines with optional summary messages.

Changes

Workspace log streaming

Layer / File(s) Summary
Backend progress pipeline
desktop/src/main/ipc.ts, desktop/src/main/log-store.ts, desktop/src/main/__tests__/log-store.test.ts, desktop/src/renderer/src/lib/types/index.ts
Progress output is batched through createLogSink, persisted with buffered write streams, completed through closeLog, and represented by optional lines and message fields.
Renderer progress buffering
desktop/src/renderer/src/lib/components/workspace/WorkspaceWizard.svelte, desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
Workspace output handlers accept batched lines or message fallback and trim accumulated output buffers to bounded sizes.
Capped log rendering
desktop/src/renderer/src/lib/components/log/LogTable.svelte
Log rows are limited by maxLines, older lines are reported as hidden, follow-tail behavior is simplified, and rows render directly from the visible slice.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkspaceCommand
  participant ProgressSink
  participant LogStore
  participant Renderer
  WorkspaceCommand->>ProgressSink: Stream formatted output
  ProgressSink->>LogStore: Append batched lines
  ProgressSink->>Renderer: Send command-progress lines
  WorkspaceCommand->>ProgressSink: Signal completion
  ProgressSink->>LogStore: Close buffered stream
  ProgressSink->>Renderer: Send completion metadata
Loading

Possibly related PRs

  • devsy-org/devsy#435: Both changes use the accumulated WorkspaceWizard.svelte output buffer.
  • devsy-org/devsy#609: Both changes address streamed output handling in workspace views and large log rendering.

Suggested labels: size/l

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately captures the main desktop log-ingest performance fix and log-line wrapping changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@desktop/src/main/ipc.ts`:
- Around line 90-93: Update done() and every workspace command completion path
to enqueue the final log line, await the asynchronous closeLog() flush, and only
then emit the sink completion event with done: true. Preserve the existing level
and CLIError handling while ensuring all listed completion sites use this
ordering.

In `@desktop/src/main/log-store.ts`:
- Around line 76-90: Update appendLog and the upstream streaming path to honor
the boolean returned by stream.write: when it returns false, pause the stdout
reader and resume it on the stream’s drain event. Thread this pause/resume
behavior through the relevant command-output reader while preserving existing
ENOENT handling and normal streaming.

In `@desktop/src/renderer/src/lib/components/log/LogTable.svelte`:
- Line 57: Update the aria-rowcount and per-row aria-rowindex bindings in
LogTable so ARIA positions account for the header row and any omitted earlier
data rows. Ensure the first visible data row receives its actual 1-based
position after applying the hidden-row offset, while preserving the header’s row
position and total row count semantics.
- Around line 81-102: Update the LogTable grid classes to use minmax(0,1fr) for
the message column, and add min-w-0 with wrap-anywhere to the message cell
containing line.message. Preserve the existing layout, styling, and whitespace
behavior while allowing long unbroken tokens to shrink and wrap without
horizontal overflow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0804eb21-c697-44c3-8f15-ba8f03395b30

📥 Commits

Reviewing files that changed from the base of the PR and between 789ddd8 and c8a282e.

📒 Files selected for processing (7)
  • desktop/src/main/__tests__/log-store.test.ts
  • desktop/src/main/ipc.ts
  • desktop/src/main/log-store.ts
  • desktop/src/renderer/src/lib/components/log/LogTable.svelte
  • desktop/src/renderer/src/lib/components/workspace/WorkspaceWizard.svelte
  • desktop/src/renderer/src/lib/types/index.ts
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte

Comment thread desktop/src/main/ipc.ts Outdated
Comment thread desktop/src/main/log-store.ts Outdated
Comment thread desktop/src/renderer/src/lib/components/log/LogTable.svelte Outdated
Comment thread desktop/src/renderer/src/lib/components/log/LogTable.svelte Outdated
- LogTable: use minmax(0,1fr) for the message column plus min-w-0 and
  wrap-anywhere so long unbroken tokens shrink and wrap instead of
  forcing horizontal overflow.
- LogTable: offset ARIA row positions for the header and hidden rows
  (aria-rowcount + 1; data rows start at hiddenCount + 2).
- ipc/log-store: flush the log file (await closeLog) before emitting the
  done event, so a post-done read can't observe partial output.
appendLog now returns the write result and LogStore exposes onDrain.
runStreaming pauses the child's stdout/stderr when onLine returns a
promise (the consumer applying backpressure) and resumes on drain, so a
command emitting logs faster than disk can't grow the write buffer
without bound. Verified with a 50k-line burst: no deadlock, UI stays
responsive.
@skevetter
skevetter merged commit 82aaaa5 into main Jul 19, 2026
28 checks passed
@skevetter
skevetter deleted the feat/desktop-log-perf branch July 19, 2026 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant