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

@HonaHona 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 ownerAugust 3, 2026 00:26
CopilotAI review requested due to automatic review settings August 3, 2026 00:26

CopilotAI 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
FileDescription
packages/session-ui/src/v2/components/prompt-input/types.tsAdds blob reference type; replaces attachment dataUrl with blob.
packages/session-ui/src/v2/components/prompt-input/store.test.tsUpdates V2 store tests for blob-backed attachments.
packages/session-ui/src/v2/components/prompt-input/prompt-input.stories.tsxUpdates stories to use blob references.
packages/session-ui/src/v2/components/prompt-input/interaction.tsThreads preview URLs + revocation through controller API.
packages/session-ui/src/v2/components/prompt-input/index.tsxRenders attachment thumbnails via previewUrl callback.
packages/session-ui/src/v2/components/prompt-input/attachments.tsImplements blob persistence, preview caching, and legacy data URL migration.
packages/desktop/src/renderer/index.tsxReplaces async storage with repository-backed persistence + drain requests.
packages/desktop/src/renderer/i18n/index.tsReads locale from new persistence API.
packages/desktop/src/preload/types.tsDefines PersistenceAPI and removes old store API types.
packages/desktop/src/preload/index.tsWires persistence IPC calls and drain request subscription.
packages/desktop/src/main/updater.tsDrains persistence before quitAndInstall.
packages/desktop/src/main/updater-controller.tsAllows async quitAndInstall and awaits it.
packages/desktop/src/main/persistence.tsAdds SQLite WAL persistence for documents + content-addressed blobs, with migration.
packages/desktop/src/main/persistence.test.tsTests documents, blob integrity checks, and Electron store import.
packages/desktop/src/main/ipc.tsAdds persistence IPC handlers + renderer drain coordination.
packages/desktop/src/main/index.tsInitializes persistence, imports legacy stores, drains on quit/relaunch/update.
packages/desktop/package.jsonRemoves @solid-primitives/storage dependency.
packages/app/test-browser/review-panel-v2-state.test.tsUpdates platform mocking to use repository persistence.
packages/app/test-browser/prompt-persistence.test.tsUpdates persistence tests to use repository abstraction.
packages/app/test-browser/prompt-attachments.test.tsAdds regression coverage for blob-backed attachments and preview stability.
packages/app/src/utils/prompt.tsAdds opencode-blob: URLs + restore path for historical data: attachments.
packages/app/src/utils/prompt.test.tsUpdates prompt extraction tests and adds restore coverage.
packages/app/src/utils/persist.tsReplaces makePersisted with repository-backed persistence + legacy migrations.
packages/app/src/utils/persist.test.tsRefocuses tests on address/normalization semantics.
packages/app/src/persistence/repository.tsAdds checkpointed repository wrapper over durable backends.
packages/app/src/persistence/repository.test.tsTests repository coalescing at checkpoint deadline.
packages/app/src/persistence/indexed-db.tsImplements IndexedDB durable repository for browsers + blob integrity checks.
packages/app/src/persistence/index.tsExports persistence primitives.
packages/app/src/persistence/checkpoint.tsAdds 500ms checkpoint controller implementation.
packages/app/src/persistence/checkpoint.test.tsTests checkpoint scheduling, coalescing, drain semantics, retry-on-drain.
packages/app/src/pages/session/use-session-commands.tsxRestores prompts using blob persistence when reverting.
packages/app/src/pages/session.tsxRestores drafts via blob persistence; reads blobs when needed.
packages/app/src/index.tsExports repository APIs from app package for desktop integration.
packages/app/src/context/server-session.tsAdds optimistic cleanup hook for attachment/object URL lifecycle.
packages/app/src/context/server-session.test.tsAdds cleanup assertions and attachment URL confirmation coverage.
packages/app/src/context/prompt-state.tsMigrates image parts to blob references and deep clones blob refs.
packages/app/src/context/platform.tsxReplaces storage with persistence, defaulting to IndexedDB repo.
packages/app/src/context/directory-sync.tsThreads optimistic cleanup capability through directory sync.
packages/app/src/components/prompt-input/submit.tsResolves blob refs to canonical data: at submission; optimistic object URLs + cleanup.
packages/app/src/components/prompt-input/submit.test.tsAdds submission coverage for blob ref resolution and command attachments.
packages/app/src/components/prompt-input/image-attachments.tsxRenders thumbnails via preview callback.
packages/app/src/components/prompt-input/history.tsDeep clones blob references for history navigation.
packages/app/src/components/prompt-input/history.test.tsUpdates history tests for blob ref cloning.
packages/app/src/components/prompt-input/build-request-parts.tsUses opencode-blob: URL placeholders for attachments.
packages/app/src/components/prompt-input/build-request-parts.test.tsUpdates tests for new attachment URL scheme.
packages/app/src/components/prompt-input/attachments.tsBlob-backed attachment persistence + preview cache + legacy migration.
packages/app/src/components/prompt-input.tsxIntegrates blob persistence into PromptInput v1 attachments and previewing.
packages/app/src/components/prompt-input-v2.tsxIntegrates blob persistence into PromptInput v2.
packages/app/src/components/dialog-fork.tsxRestores fork prompts via blob persistence before navigation.
packages/app/package.jsonRemoves @solid-primitives/storage dependency.
bun.lockRemoves @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
MemberAuthor

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

@HonaHona closed this Aug 3, 2026
Sign up for freeto 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

@Hona