Skip to content

perf(core): stop writing duplicate snapshot events in local mode - #47661

Closed
gmhelmold wants to merge 5 commits into
anomalyco:devfrom
gmhelmold:perf-lazy-persist-gate
Closed

gmhelmold wants to merge 5 commits into
anomalyco:devfrom
gmhelmold:perf-lazy-persist-gate

Conversation

@gmhelmold

@gmhelmold gmhelmold commented Sep 6, 2026

Copy link
Copy Markdown

What

Two performance fixes for local single-machine usage, no capability removed.

1. Lazy-load CLI commands (refactor(opencode))

The CLI entrypoint statically imported every command module (server, session, sdk, config schemas) at process start — tens of seconds and ~224MB RSS even for --version. Commands now register through a delegating module (cli/lazy-command.ts) that loads the real implementation only when invoked. Also decoupled cli/ui from the effect import.

--version, same commit, fresh worktree before after delta
instructions retired 10.3B 5.4B -46%
peak RSS 224MB 132MB -41%
user CPU 3.4s 1.6s -52%

2. Stop writing duplicate snapshot events in local mode (perf(core))

updateMessage/updatePart published full snapshot events to the durable event table on every change — a single 23MB message re-rendered 90x wrote ~2GB of redundant JSON to the event log (user DB grew to 7GB, 6.4GB of it the event table). The only consumer of those durable rows is the experimental workspaces sync (off by default); local UI/SSE/projection read the projected tables.

Adds PublishOptions.persist (default true = unchanged). updateMessage/updatePart pass persist: experimentalWorkspaces so local installs stop appending to the event log while still projecting tables and notifying SSE. With workspaces enabled, behavior is byte-identical; the bridge only emits sync envelopes for events carrying a durable marker.

E2E write-path (90x growing part, up to 24MB) durable local delta
time 10.2s 5.5s -46%
durable bytes written ~2GB 0 -100%

Combine with an optional db compact one-shot to reclaim rows already written (guarded against experimentalWorkspaces, which requires contiguous seq for sync replay).

Why

  • CLI startup dominated by eager imports unrelated to the invoked command.
  • Local event log grows superlinearly per streaming turn while nothing local reads it.

Tests

  • core: event 44, event-persist-gate 2, event-compact 2 — 48 pass
  • opencode: session 8, persist-gate 2 (OFF+ON), cli/error 6 — 16 pass
  • httpapi-event (SSE) — 3 pass
  • E2E full prompt loop (prompt.test) — projects tables, 0 durable rows
  • typecheck: core + opencode, 0 errors. Turbo locally ENOEXEC (broken env); CI runs the full workspace check.

Reviewer notes

  • The persist:false branch passes projectors a placeholder durable.seq = -1; no current projector reads seq (they upsert by entity id). Documented in core/event.ts.
  • persist:false skips the commit hook by design; no caller combines them.
  • Follow-up candidate: retry-awareness for db compact gaps (needs design before re-enabling with workspaces).

Gustavo Schneiter added 2 commits September 6, 2026 17:46
The entrypoint statically imported every command module (server, session,
sdk, config schemas) at process start, costing tens of seconds and ~200MB
RSS even for fast paths like --version. Register commands through a
delegating module that only loads the real implementation when yargs
parses an invocation that exercises it.

Measured: --version instructions 10.4B -> 5.6B (-46%), peak RSS
224MB -> 132MB (-41%), user CPU 3.4s -> 1.9s (-44%).

Also decouple UI.cancelled-error from the effect import so the ui module
loads without pulling in the full effect runtime.
…mode

updateMessage/updatePart published full snapshot events to the durable
event table on every change, duplicating the complete payload per update.
A single 23MB message re-rendered 90x wrote ~2GB of redundant JSON to the
event log (the user's opencode.db grew to 7GB, 6.4GB of it the event
table). The only consumer of these durable rows is the experimental
workspaces sync (off by default); the local UI/SSE/projection read the
projected message/part tables.

Add PublishOptions.persist (default true = unchanged). updateMessage/
updatePart pass persist: experimentalWorkspaces so local installs stop
appending to the event log while still projecting and notifying. With
workspaces enabled the behavior is byte-identical to before. The bridge
only emits sync envelopes for events carrying a durable marker, so local
events are never observed by cross-instance sync.

Measured: write-path E2E (90x growing part up to 24MB) 10.2s -> 5.5s
(-46%); publish micro-bench -92%; durable bytes per turn 100% eliminated.

Add db compact command as an optional one-shot tool to reclaim rows
already written (guarded against experimentalWorkspaces, which requires
contiguous seq for sync replay).
@github-actions github-actions Bot added needs:title needs:compliance This means the issue will auto-close after 2 hours. labels Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Hey! Your PR title perf(core): stop writing duplicate snapshot events in local mode doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Based on my search results, I found two potentially related PRs (excluding the current PR #47661):

Related PRs:

  1. feat(storage): add safe database maintenance controls (PR feat(storage): add safe database maintenance controls #41711)

  2. fix(opencode): evict message diff patches from the event payload (PR fix(opencode): evict message diff patches from the event payload #42771)

These are not exact duplicates but address related concerns about event storage optimization and database maintenance.

Gustavo Schneiter added 3 commits September 6, 2026 17:58
The publish timing bench was flaky under machine load and duplicated the
behavioral coverage already provided by event-persist-gate. The measured
speedup (write-path E2E -46%, publish micro-bench -92%) is captured in the
perf commit message instead of as a timing assertion.
…tract

Reviewers need the invariant spelled out: the local-only publish path
supplies an inert seq placeholder to projectors (none read it) and skips
commit hooks by design (no caller combines them). Adds the rationale for
gating message/part persistence on experimentalWorkspaces in session.ts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs:compliance This means the issue will auto-close after 2 hours. needs:title

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant