Files
goodbuddy/docs/development/database-migrations.md
T
mesalogo 4c7193e1ea fix: avoid history reclamation during structure-only upgrades
Ordinary database schema upgrades previously ran VACUUM on free pages and
showed misleading history-cleanup progress. Separate structure upgrades from
required legacy conversions, retaining reclamation intent only for retries
within the same startup. Preserve released data without new schema versions
or persistent migration markers; Agent and normal writes remain unchanged.

Add real SQLite and Worker regressions, bilingual operation-specific startup
copy, and migration documentation. Synchronize native Electron drag input
with actual resize state without weakening layout assertions.

Validation: 47 focused migration tests passed; typecheck, lint and diff checks
passed. Final full suite: 5137 passed, 74 skipped, 2 unrelated Electron failures.
Both failing Electron tests passed an isolated rerun; full CI remains unverified.

Release note: 修复普通数据库结构升级再次整理历史和回收空间的问题;仅在需要转换旧数据时执行相关处理,启动页准确显示当前升级操作。
2026-09-26 12:42:08 +08:00

6.1 KiB
Raw Permalink Blame History

数据库迁移与大数据升级指南

本文约束已发布用户数据的数据库升级。具体字段、存储表示和功能行为由对应功能文档负责; 本文不建立通用迁移框架、回滚引擎或独立状态日志。

先确认问题和数据边界

  1. 先只读统计文件大小、WAL、页数、空闲页、表与字段的字节数,不先删除或压缩数据库。
  2. 区分有效内容、重复存储、索引和已释放但未回收的空间。空闲页少时,单独 VACUUM 不能消除重复记录。
  3. 查清实际写入、读取、删除、重放与恢复入口。修改 Runtime 相关持久化时,同时检查 本机和 GoodBuddy Agent 的桌面路径,不假设一个入口覆盖另一个入口。
  4. 明确用户必须保留的信息、稳定 ID、顺序、时间、引用与远程去重字段。 分支内未发布、可安全重建的内部记录不需要兼容机制。

实现原则

  • 先修新增数据路径,再修历史数据。历史转换与新写入尽量使用同一套转换函数。
  • 普通 schema 变更使用既有 PRAGMA user_version 和 SQLite 事务; 已升级 schema 不允许旧客户端强行打开,不通过回写版本号支持降级。
  • 将结构升级、旧内容转换和空间回收分别判断。结构版本落后不代表需要整理历史; 普通更新产生的空闲页也不是旧数据证据。只有明确的旧内容转换才允许检查空闲页并 执行 VACUUM,后续新增结构迁移不自动继承全库回收。启动页按实际操作显示状态。
  • 同一次启动的重试可在内存中保留已确认的转换/回收决定;退出后重新根据数据库 schema 和未转换内容判断,不为仅剩空闲页增加持久化标记或重复启动回收。
  • 大数据转换不得同步阻塞 Electron Main 或 Renderer。放到工作线程,在业务连接 打开前处理,并使用应用的共享字体、主题与页面组件显示进度和可执行的失败提示。
  • 根据实测设置有界读取批次与缓存,批次提交后再报告进度。保持 SQLite 事务与 checkpoint,不靠关闭同步落盘、取消约束或一次性读入整库提速。
  • 从已提交数据直接重试和还原即可完成的迁移,不增加 durable snapshot、journal、 receipt 或多阶段恢复状态。退出及失败至少回滚未提交批次,保留已完成部分。
  • 差量编码必须能精确还原原始事件。更新内容、删除或重排都要覆盖;涉及远程来源时, 去重、冲突判断、消息投影和恢复序号不得失效。
  • 释放数据库页和归还文件系统空间是不同步骤。先减少实际存储内容,再执行需要的 空间回收;保留足够临时空间,磁盘不足时明确失败并允许重试。
  • SQLite 同库写入是串行的。是否增加读取/转换并行,应由实际读取、解析、写入、 提交与 checkpoint 的耗时决定,不把增加写线程当作默认优化。

验证与备份

合成回归

覆盖正常转换、无变化记录、重复打开、内容修订、失败事务和退出续跑。数据库大小问题 必须同时验证新增写入的增长量、旧记录可还原性以及文件实际缩小,而不只检查 schema。 对象比较必须覆盖真实生产形状和 JSON 往返,尤其是可选字段值为 undefined 时与字段缺省 的差异。再次修复已发布迁移产生的数据时,提高目标 schema,并覆盖已升级用户的入口; 不能只处理尚未迁移的旧库。 仅修改升级调度、不改变存储格式时不增加 schema。回归必须覆盖已完成历史转换的库 在后续结构升级时仍有空闲页,确认不重新转换或 VACUUM,并验证真实 Worker、 同次启动重试和下一次启动的区别。

真实旧库

  1. 关闭占用源数据库的应用,确认 WAL 状态。没有 WAL 时可复制已关闭的数据库; 有活动 WAL 时使用 SQLite 一致性备份机制,不只复制主文件。
  2. 备份到空间足够的位置,记录字节数并校验 SHA-256。备份和测试副本分离, 不直接在唯一原件或唯一备份上试跑。
  3. 使用当前源码或用户要求的 portable 正常启动入口执行升级,不用手工 SQL 代替 产品升级过程。退出一次后重新启动,验证已提交数据可以继续处理。
  4. 比较升级前后消息、任务、成果等用户表的内容;对转换事件逐条还原后比较原始内容、 ID、顺序、时间和去重来源,不只比较行数。日志只能包含统计和断言结果,不记录正文。
  5. 运行数据库完整性检查,记录主文件和 WAL 的实际字节数、回收比例、耗时、平台、 源 schema 与目标 schema。确认原件和备份未被改写。
  6. 运行 npm test、npm run typecheck、npm run lint;生产构建变化按开发验证规则 检查,发布准备则遵循发布手册,不重复本地打包。
  7. 用真实模型模拟正常用户任务,允许主代理与子代理按需调用工具、修改独立测试项目、 运行测试和处理失败;只读纯文字模型调用不能证明工具事件增长正常。记录实际提供商 请求数与统计范围,不把 Runtime 用量上报数当成全部 HTTP 请求数。

发布与维护

发布说明应告知升级范围、可能耗时、退出重试、额外磁盘空间和降级边界。不得把一个测试库 的回收比例当作所有用户都能获得的保证。功能文档记录具体转换规则,进度文档记录实际 证据和未完成项;未经验证的阶段不能标为完成。

已有实例

  • Desktop 0.13.1:子任务累计过程重复保存导致数据库膨胀,转换到 schema 34 的差量事件 表示,并回收空间。具体规则见 执行记录存储与升级回收, 数据保留与真实旧库验证见工作栏进度。
  • Desktop 0.13.2:schema 35 同时整理旧快照和 schema 34 的重复工具块,工具字段按 JSON 可保存的值比较,不因 undefined 被省略而误判变化。