Skip to content

Durable event table bloats from repeated turn diff payloads #48641

Closed as not planned

Description

@AceRothstein71

Description

The durable event table grows far faster than the actual conversation content, because full git patch text is written into a new immutable event row on every turn update.

SessionSummary.summarize attaches the turn's full diffs to the user message (summary.diffs, including each file's patch string). It is forked on every step-finish for the same user message, so each run republishes the entire message — patches included — as a brand new message.updated.1 row.

I sampled 400 message.updated.1 rows in my local database:

rows avg payload
25% (carrying summary) 400.8 KB
75% (no summary) 0.55 KB

Extrapolated across the table that is roughly 49 GB of a 79 GB database, and it is almost entirely the same patch text written over and over. The repeated serialization also blocks the event loop during long turns.

Steps to reproduce

  1. Run a session where the assistant edits files across several steps, so summarize runs more than once for the same user message.
  2. Inspect the event store:
SELECT length(data) FROM event WHERE type = 'message.updated.1' ORDER BY length(data) DESC LIMIT 20;
  1. Note that the large rows are repeats of the same summary.diffs payload for the same message, not new content.

Operating System

Linux

Notes

The diff metadata already has other homes (session.summary_diffs, and revert.ts writes to session_diff storage), so the per-turn patch text does not need to live in a re-serialized message event. I have a fix for this and will open a PR against dev.

Activity

  1. github-actions commented on Sep 12, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    If your report is specifically the summary.diffs angle (which it appears to be), #42748 is the closest match and already has an assignee and discussion.

  2. AceRothstein71 commented on Sep 19, 2026

    @AceRothstein71
    Author

    Fixed by PR #48638 (the branch already contains the rebased commits of the closed PR #48537). SessionSummary.summarize no longer re-attaches patch text to the user message: the durable baseline carries diffs: [] (packages/opencode/src/session/summary.ts:243), patch payloads move to a dedicated message.diff.updated event (summary.ts:249), unchanged diffs are skipped (summary.ts:212), and superseded rows are tombstoned by the projector (packages/core/src/session/projector.ts:247-295). Commits 3086653aff, 35310f77c4, 9c0aa5931e, e2772c810e. Tests: packages/opencode/test/server/session-message-diff-events.test.ts, packages/core/test/session-projector.test.ts. Follow-up on PR #48638 (head 7063bdd9ea).

  3. askaramirov commented on Oct 16, 2026

    @askaramirov

    Same issue here, with a deterministic repro and numbers from a long-lived install:

    Version: opencode 1.18.34 (linux-x64). Not checked on 2.x yet.

    What happens

    During a turn, every time the user message's summary is updated (after
    each tool step), opencode appends a message.updated.1 row to the event
    table. Its data holds info.summary.diffs: the full patch of every file
    changed so far in the turn. So the k-th update stores k diffs, and a turn
    that changes n files of size s writes about s·n(n+1)/2 bytes of events,
    not s·n.

    Minimal repro (a scripted model, so it's deterministic)

    1. Scratch environment: HOME, XDG_CONFIG_HOME, XDG_DATA_HOME,
      XDG_STATE_HOME, XDG_CACHE_HOME, TMPDIR all in a temp dir; a git
      repo as the working dir.
    2. A provider using @ai-sdk/openai-compatible, pointing at a local
      OpenAI-compatible server that answers the first 6 tool-enabled requests
      with one bash tool call each, head -c 150000 /dev/urandom | base64 > f<i>.txt, then a final text reply. (A ~60-line Python script; we can
      attach it.)
    3. opencode run --pure --format json -m mock/scripted "write six files".
    4. Query $XDG_DATA_HOME/opencode/opencode.db:
    SELECT seq, length(data),
           json_array_length(json_extract(data,'$.info.summary.diffs'))
    FROM event WHERE type = 'message.updated.1' ORDER BY seq;

    Result (measured, 2026-10-15)

    • Files written: 6, 1,215,792 bytes in total.
    • message.updated.1 events: 29 rows, 4,384,656 bytes: 3.6× the data changed.
    • The user message's updates carry 1, 2, 3, 4, 5 and 6 diffs: 208 KB, 416 KB, 625 KB, 833 KB, 1.04 MB, 1.25 MB. Each one repeats all the earlier patches.
    • All other event types together: about 30 KB.

    Effect on a real, long-lived install (one user's machine, about 28 concurrent sessions)

    • opencode.db is 69 GB and grows 3–7 GB a day.
    • The event table is about 92% of the bytes. Nearly all of it is message.updated.1 rows whose size is info.summary.diffs[*].patch. One row was 47.9 MB; one session had 330 such rows for 83 messages.
    • summary.diffs is also 96% of the message table's bytes.
    • With many processes on one WAL database, writes of rows this size hold the write lock long enough that other processes hit busy_timeout = 5000 (hard-coded) and fail with LockTimeoutError: database is locked.
    • There is no free space to reclaim (freelist 0), so VACUUM does not help. Only deleting rows does.

    Expected

    The event stream stays roughly linear in the work done. Options:

    • emit only the changed diff (a delta) in each event;
    • keep summary diffs out of message.updated events, storing them once (or only file names and stats) and computing patches on demand from snapshots;
    • or compact superseded message.updated events for the same message.

    Also useful

    • A way to configure busy_timeout, and/or retrying LockTimeoutError on the write paths (it is marked isRetryable but a turn still failed on it).
    • A supported way to prune or archive old sessions' events.
  4. neriousy commented on Oct 20, 2026

    @neriousy
    Member

    Thanks for the detailed report! V2 no longer uses the repeated message-summary patch events described here; starting with 2.0.3, turn diffs are computed from snapshots when requested instead, though this does not repair existing V1 database growth. Update with opencode upgrade or npm i -g @opencode/cli@latest (desktop: update the app). Please reopen or comment if you see the same repeated patch-event growth on that version or newer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions