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: 修复普通数据库结构升级再次整理历史和回收空间的问题;仅在需要转换旧数据时执行相关处理,启动页准确显示当前升级操作。
84 lines
6.1 KiB
Markdown
84 lines
6.1 KiB
Markdown
# 数据库迁移与大数据升级指南
|
||
|
||
本文约束已发布用户数据的数据库升级。具体字段、存储表示和功能行为由对应功能文档负责;
|
||
本文不建立通用迁移框架、回滚引擎或独立状态日志。
|
||
|
||
## 先确认问题和数据边界
|
||
|
||
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`;生产构建变化按开发验证规则
|
||
检查,发布准备则遵循[发布手册](./release-runbook.md),不重复本地打包。
|
||
7. 用真实模型模拟正常用户任务,允许主代理与子代理按需调用工具、修改独立测试项目、
|
||
运行测试和处理失败;只读纯文字模型调用不能证明工具事件增长正常。记录实际提供商
|
||
请求数与统计范围,不把 Runtime 用量上报数当成全部 HTTP 请求数。
|
||
|
||
## 发布与维护
|
||
|
||
发布说明应告知升级范围、可能耗时、退出重试、额外磁盘空间和降级边界。不得把一个测试库
|
||
的回收比例当作所有用户都能获得的保证。功能文档记录具体转换规则,进度文档记录实际
|
||
证据和未完成项;未经验证的阶段不能标为完成。
|
||
|
||
### 已有实例
|
||
|
||
- Desktop 0.13.1:子任务累计过程重复保存导致数据库膨胀,转换到 schema 34 的差量事件
|
||
表示,并回收空间。具体规则见
|
||
[执行记录存储与升级回收](../features/assistant-workbar/execution-history-storage.md),
|
||
数据保留与真实旧库验证见[工作栏进度](../features/assistant-workbar/progress.md)。
|
||
- Desktop 0.13.2:schema 35 同时整理旧快照和 schema 34 的重复工具块,工具字段按
|
||
JSON 可保存的值比较,不因 `undefined` 被省略而误判变化。
|