Skip to content

fix(app): eliminate persistence write amplification - #40197

Closed
Hona wants to merge 2 commits into
anomalyco:devfrom
Hona:persistence-v2
Closed

fix(app): eliminate persistence write amplification#40197
Hona wants to merge 2 commits into
anomalyco:devfrom
Hona:persistence-v2

Conversation

@Hona

@Hona Hona commented Aug 3, 2026

Copy link
Copy Markdown
Member

Summary

  • replace setter-coupled makePersisted writes with a shared repository and fixed 500ms checkpoint deadline
  • persist desktop documents and content-addressed blobs in SQLite WAL, with IndexedDB parity for browsers
  • keep prompt drafts and history reference-only, materializing base64 only at the existing app API boundary
  • migrate legacy Electron stores and embedded data URLs, preserve read-your-writes consistency, and drain renderer checkpoints during shutdown
  • preserve image history, fork, undo, duplicate attachment semantics, optimistic previews, and object URL ownership across confirmation
  • retain the existing 30-day/newest-100 draft policy and reclaim unreferenced desktop blobs at startup

Verification

  • workspace pre-push typecheck: 30 packages passed
  • app unit and browser persistence/attachment regression tests passed
  • desktop SQLite migration, cleanup, and updater tests passed
  • session-ui test suite passed
  • app production build passed
  • manually verified stable image previews and idle main-process CPU after removing the repeated blob-read loop

@Hona
Hona requested a review from Brendonovich as a code owner August 3, 2026 00:26
Copilot AI review requested due to automatic review settings August 3, 2026 00:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR reworks app/desktop persistence to reduce write amplification by introducing a checkpointed repository abstraction and migrating prompt attachments from inline data: URLs to content-addressed blob references that are persisted durably (SQLite WAL on desktop, IndexedDB in browsers).

Changes:

  • Introduces a Repository / DurableRepository layer with a 500ms checkpoint controller to coalesce frequent writes.
  • Migrates prompt attachment handling to persist blobs by digest and render previews via object URLs, materializing data: only at the existing submission boundary.
  • Adds desktop SQLite persistence + IPC API, including renderer drain coordination during shutdown and before updater installs.

Reviewed changes

Copilot reviewed 50 out of 51 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/session-ui/src/v2/components/prompt-input/types.ts Adds blob reference type; replaces attachment dataUrl with blob.
packages/session-ui/src/v2/components/prompt-input/store.test.ts Updates V2 store tests for blob-backed attachments.
packages/session-ui/src/v2/components/prompt-input/prompt-input.stories.tsx Updates stories to use blob references.
packages/session-ui/src/v2/components/prompt-input/interaction.ts Threads preview URLs + revocation through controller API.
packages/session-ui/src/v2/components/prompt-input/index.tsx Renders attachment thumbnails via previewUrl callback.
packages/session-ui/src/v2/components/prompt-input/attachments.ts Implements blob persistence, preview caching, and legacy data URL migration.
packages/desktop/src/renderer/index.tsx Replaces async storage with repository-backed persistence + drain requests.
packages/desktop/src/renderer/i18n/index.ts Reads locale from new persistence API.
packages/desktop/src/preload/types.ts Defines PersistenceAPI and removes old store API types.
packages/desktop/src/preload/index.ts Wires persistence IPC calls and drain request subscription.
packages/desktop/src/main/updater.ts Drains persistence before quitAndInstall.
packages/desktop/src/main/updater-controller.ts Allows async quitAndInstall and awaits it.
packages/desktop/src/main/persistence.ts Adds SQLite WAL persistence for documents + content-addressed blobs, with migration.
packages/desktop/src/main/persistence.test.ts Tests documents, blob integrity checks, and Electron store import.
packages/desktop/src/main/ipc.ts Adds persistence IPC handlers + renderer drain coordination.
packages/desktop/src/main/index.ts Initializes persistence, imports legacy stores, drains on quit/relaunch/update.
packages/desktop/package.json Removes @solid-primitives/storage dependency.
packages/app/test-browser/review-panel-v2-state.test.ts Updates platform mocking to use repository persistence.
packages/app/test-browser/prompt-persistence.test.ts Updates persistence tests to use repository abstraction.
packages/app/test-browser/prompt-attachments.test.ts Adds regression coverage for blob-backed attachments and preview stability.
packages/app/src/utils/prompt.ts Adds opencode-blob: URLs + restore path for historical data: attachments.
packages/app/src/utils/prompt.test.ts Updates prompt extraction tests and adds restore coverage.
packages/app/src/utils/persist.ts Replaces makePersisted with repository-backed persistence + legacy migrations.
packages/app/src/utils/persist.test.ts Refocuses tests on address/normalization semantics.
packages/app/src/persistence/repository.ts Adds checkpointed repository wrapper over durable backends.
packages/app/src/persistence/repository.test.ts Tests repository coalescing at checkpoint deadline.
packages/app/src/persistence/indexed-db.ts Implements IndexedDB durable repository for browsers + blob integrity checks.
packages/app/src/persistence/index.ts Exports persistence primitives.
packages/app/src/persistence/checkpoint.ts Adds 500ms checkpoint controller implementation.
packages/app/src/persistence/checkpoint.test.ts Tests checkpoint scheduling, coalescing, drain semantics, retry-on-drain.
packages/app/src/pages/session/use-session-commands.tsx Restores prompts using blob persistence when reverting.
packages/app/src/pages/session.tsx Restores drafts via blob persistence; reads blobs when needed.
packages/app/src/index.ts Exports repository APIs from app package for desktop integration.
packages/app/src/context/server-session.ts Adds optimistic cleanup hook for attachment/object URL lifecycle.
packages/app/src/context/server-session.test.ts Adds cleanup assertions and attachment URL confirmation coverage.
packages/app/src/context/prompt-state.ts Migrates image parts to blob references and deep clones blob refs.
packages/app/src/context/platform.tsx Replaces storage with persistence, defaulting to IndexedDB repo.
packages/app/src/context/directory-sync.ts Threads optimistic cleanup capability through directory sync.
packages/app/src/components/prompt-input/submit.ts Resolves blob refs to canonical data: at submission; optimistic object URLs + cleanup.
packages/app/src/components/prompt-input/submit.test.ts Adds submission coverage for blob ref resolution and command attachments.
packages/app/src/components/prompt-input/image-attachments.tsx Renders thumbnails via preview callback.
packages/app/src/components/prompt-input/history.ts Deep clones blob references for history navigation.
packages/app/src/components/prompt-input/history.test.ts Updates history tests for blob ref cloning.
packages/app/src/components/prompt-input/build-request-parts.ts Uses opencode-blob: URL placeholders for attachments.
packages/app/src/components/prompt-input/build-request-parts.test.ts Updates tests for new attachment URL scheme.
packages/app/src/components/prompt-input/attachments.ts Blob-backed attachment persistence + preview cache + legacy migration.
packages/app/src/components/prompt-input.tsx Integrates blob persistence into PromptInput v1 attachments and previewing.
packages/app/src/components/prompt-input-v2.tsx Integrates blob persistence into PromptInput v2.
packages/app/src/components/dialog-fork.tsx Restores fork prompts via blob persistence before navigation.
packages/app/package.json Removes @solid-primitives/storage dependency.
bun.lock Removes @solid-primitives/storage and updates patched/trusted ordering.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +283 to +288
const persistence = platform?.persistence ?? getIndexedDBRepository()
return Promise.all([
persistence.remove(address(config)),
...(config.legacyStorageNames ?? []).map((storage) => persistence.remove({ storage, key: config.key })),
...(config.legacy ?? []).map((key) => persistence.remove({ storage: LEGACY_STORAGE, key })),
]).then(() => undefined)
Comment on lines +58 to +65
(error) => {
inFlight = false
idleWaiters.splice(0).forEach((resolve) => resolve())
dirtySince = Math.min(attemptedSince ?? Date.now(), dirtySince ?? Number.POSITIVE_INFINITY)
const pending = waiters
waiters = []
pending.forEach((waiter) => waiter.reject(error))
},
@Hona

Hona commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

Superseded by the narrower draft-only implementation in #40207.

@Hona Hona closed this Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants