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: 修复普通数据库结构升级再次整理历史和回收空间的问题;仅在需要转换旧数据时执行相关处理,启动页准确显示当前升级操作。
This commit is contained in:
@@ -18,6 +18,11 @@
|
||||
- 先修新增数据路径,再修历史数据。历史转换与新写入尽量使用同一套转换函数。
|
||||
- 普通 schema 变更使用既有 `PRAGMA user_version` 和 SQLite 事务;
|
||||
已升级 schema 不允许旧客户端强行打开,不通过回写版本号支持降级。
|
||||
- 将结构升级、旧内容转换和空间回收分别判断。结构版本落后不代表需要整理历史;
|
||||
普通更新产生的空闲页也不是旧数据证据。只有明确的旧内容转换才允许检查空闲页并
|
||||
执行 `VACUUM`,后续新增结构迁移不自动继承全库回收。启动页按实际操作显示状态。
|
||||
- 同一次启动的重试可在内存中保留已确认的转换/回收决定;退出后重新根据数据库
|
||||
schema 和未转换内容判断,不为仅剩空闲页增加持久化标记或重复启动回收。
|
||||
- 大数据转换不得同步阻塞 Electron Main 或 Renderer。放到工作线程,在业务连接
|
||||
打开前处理,并使用应用的共享字体、主题与页面组件显示进度和可执行的失败提示。
|
||||
- 根据实测设置有界读取批次与缓存,批次提交后再报告进度。保持 SQLite 事务与
|
||||
@@ -40,6 +45,9 @@
|
||||
对象比较必须覆盖真实生产形状和 JSON 往返,尤其是可选字段值为 `undefined` 时与字段缺省
|
||||
的差异。再次修复已发布迁移产生的数据时,提高目标 schema,并覆盖已升级用户的入口;
|
||||
不能只处理尚未迁移的旧库。
|
||||
仅修改升级调度、不改变存储格式时不增加 schema。回归必须覆盖已完成历史转换的库
|
||||
在后续结构升级时仍有空闲页,确认不重新转换或 `VACUUM`,并验证真实 Worker、
|
||||
同次启动重试和下一次启动的区别。
|
||||
|
||||
### 真实旧库
|
||||
|
||||
@@ -71,5 +79,5 @@
|
||||
表示,并回收空间。具体规则见
|
||||
[执行记录存储与升级回收](../features/assistant-workbar/execution-history-storage.md),
|
||||
数据保留与真实旧库验证见[工作栏进度](../features/assistant-workbar/progress.md)。
|
||||
- 当前未发布源码:schema 35 同时整理旧快照和 schema 34 的重复工具块,工具字段按
|
||||
- Desktop 0.13.2:schema 35 同时整理旧快照和 schema 34 的重复工具块,工具字段按
|
||||
JSON 可保存的值比较,不因 `undefined` 被省略而误判变化。
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
处理速度;处理时间取决于历史数据量与磁盘速度,不承诺固定几分钟。用户可以退出,
|
||||
下次启动重新检查并继续转换未完成的数据;完成后回收空闲页并进入应用。失败时留在
|
||||
该页提供重试与退出,磁盘空间不足会明确说明,不删除聊天来腾出空间。
|
||||
以上转换和回收只针对仍需转换的旧格式数据;普通结构升级使用独立状态,不宣称正在
|
||||
清理子任务历史,也不会仅因数据库有空闲页而执行全库回收。
|
||||
|
||||
## 存储规则
|
||||
|
||||
@@ -94,7 +96,10 @@ Renderer 合并时独立接收 Main 的置顶属性,即使本地正文更新
|
||||
|
||||
启动检查只根据旧 schema 或仍未转换的笔记正文决定是否进入升级流程;正常写入、更新
|
||||
与删除产生的 SQLite 空闲页不是旧格式证据。迁移后新增数据使用当前格式,不因正常使用
|
||||
再次迁移。同一次启动已进入的迁移流程可重试压缩;若正文已转换、仅压缩中断,退出后
|
||||
再次迁移。进入升级流程后仍分别判断结构、笔记转换与空间回收,不把旧 schema 当作
|
||||
回收许可。回收只用于实际存在旧子任务事件(schema < 35)、旧活动记录(schema < 44)
|
||||
或旧笔记正文的转换;例如 schema 45/46 升到当前结构不会自动转换历史或 `VACUUM`。
|
||||
同一次启动在内存中保留这一决定,只有原本需要回收的流程可重试压缩;若正文已转换、仅压缩中断,退出后
|
||||
重开不单凭空闲页重新进入迁移,剩余页由 SQLite 后续写入复用。不新增持久化迁移标记。
|
||||
|
||||
桌面在打开业务数据库之前启动独立 Node 工作线程,使用同一套编码和还原函数转换旧库。
|
||||
@@ -108,8 +113,8 @@ Renderer 合并时独立接收 Main 的置顶属性,即使本地正文更新
|
||||
在转换前复制整份膨胀内容。回收仍需要额外可用磁盘空间,失败保留已提交转换并允许重试。
|
||||
|
||||
子任务整理对应 schema 35;schema 34 用户也会执行该整理,成功后后续启动直接跳过。
|
||||
继续复用 schema 34 的子任务事件部分索引和同一差量表示。当前业务初始化随后在事务中
|
||||
升级为 schema 36,新增 `pinned INTEGER NOT NULL DEFAULT 0 CHECK(pinned IN (0, 1))`,
|
||||
继续复用 schema 34 的子任务事件部分索引和同一差量表示。业务初始化随后在事务中
|
||||
执行后续结构迁移,其中 schema 36 新增 `pinned INTEGER NOT NULL DEFAULT 0 CHECK(pinned IN (0, 1))`,
|
||||
已有会话默认未置顶,保留原消息与时间戳;失败回滚本次字段迁移。
|
||||
低于该 schema 的旧版客户端不能重新打开已经升级的数据库。需要回退客户端时,必须使用
|
||||
升级前备份,不要修改 `user_version` 强行绕过版本检查。
|
||||
@@ -120,6 +125,8 @@ Renderer 合并时独立接收 Main 的置顶属性,即使本地正文更新
|
||||
- 工具 fixture 必须含生产对象会携带的 `undefined` 可选字段,并经过实际 JSON 写入/重读。
|
||||
同时验证旧完整事件和 schema 34 差量事件的整理、混合重试及无变化记录不被重写。
|
||||
- 验证远程重复事件、冲突事件,以及消息更新失败时事件和过程缓存的事务回滚。
|
||||
- 覆盖已完成历史转换、但正常聊天更新产生空闲页的后续结构升级:无转换进度、无
|
||||
`VACUUM`,会话、笔记、任务事件与完整性不变;验证真实 Worker 和同次启动重试。
|
||||
- 在真实旧库的已核验备份副本上运行 portable 的正常启动入口,核对事件逐条等价、
|
||||
其他用户表内容、数据库完整性和实际文件大小,不能只统计空闲页或只测试内存 fixture。
|
||||
- 同时验证真实本地 Runtime 和共享 Linux Host 的桌面持久化路径。真实场景必须让主代理
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# 工作栏实现与验证进度
|
||||
|
||||
## 2026-09-26 结构升级与历史回收分离(未发布)
|
||||
|
||||
- schema 45/46 的回归先复现普通结构升级执行 `VACUUM`,修复后不转换历史、
|
||||
不回收正常空闲页,保留聊天、笔记、事件及 SQLite 完整性。
|
||||
- 启动决定在内存中分别保留笔记转换与回收需求,Worker 重试复用原决定;
|
||||
不增加 schema 或持久标记,不修改正常写入。规则见
|
||||
[旧库升级](./execution-history-storage.md#旧库升级)。
|
||||
- 真实 Node Worker 验证结构升级、旧笔记转换、取消与重试、文件大小和内容保留;
|
||||
启动调度回归验证允许/禁止回收的两种重试,中英文页面区分实际操作。
|
||||
六个定向测试文件 **47 项通过**,类型检查、lint 和 `git diff --check` 通过。
|
||||
- 完整 `npm test`:**5,138 通过、74 跳过、1 失败**。未修改的
|
||||
`tests/magic-notes-layout.electron.test.ts` 原生拖拽断言失败:首次索引列保持 168,
|
||||
单独复跑则 AI 列保持 280,均未达到拖动后的预期宽度。该 fixture 直接加载
|
||||
Magic Notes 和模拟 API,不运行本次存储升级路径。随后修正测试的原生输入同步:
|
||||
等待焦点、拖拽建立、宽度更新及结束,并明确左键按下状态,未弱化断言或改变产品
|
||||
布局;独立复跑及随后完整测试中的该用例通过。
|
||||
- 最终完整重跑:**5,137 通过、74 跳过、2 失败**,迁移相关及布局拖拽均通过。
|
||||
`inline-help.electron.test.ts` 超时,`overlay-layering.electron.test.ts` 子进程退出码
|
||||
为 null;这两个未修改的用例随后一起独立复跑 **2 项通过**,未提高超时或修改断言。
|
||||
最终 typecheck、lint、`git diff --check` 均通过。独立复跑不替代完整测试结果,
|
||||
不宣称本次全量或 CI 全绿。
|
||||
- 本机与托管 SSH 历史共用 Desktop 数据库升级路径;Agent、Runtime、模型桥和
|
||||
远程协议未变,不需要部署 Host 补丁。本次未调用模型,未改写用户数据库,
|
||||
尚无本次修复的 CI 或发布验证。
|
||||
|
||||
## 2026-09-21 重复历史迁移补修(Desktop 0.13.9 候选)
|
||||
|
||||
- 0.13.8 的检查仍在已有笔记时把 `freelist_count > 0` 当作迁移依据;正常聊天更新
|
||||
|
||||
@@ -467,8 +467,13 @@ Electron cases with substituted model output; live-provider checks remain opt-in
|
||||
migration and backup requirements are defined in the
|
||||
[storage contract](./technical-design.md#file-storage-and-writes).
|
||||
- The startup worker converts one entry per transaction and verifies hydrated
|
||||
readback before clearing the legacy payload. Reclamation checks the freelist
|
||||
on every attempt, including retries after conversion has finished. Committed
|
||||
readback before clearing the legacy payload. The original implementation checked
|
||||
the freelist on every attempt. This was narrowed by the September 21 startup
|
||||
fix and the September 26 structure-upgrade fix: only a required legacy
|
||||
conversion enables reclamation, with its decision retained for same-startup
|
||||
retries, not later launches. Current rules are in the
|
||||
[migration contract](./technical-design.md#schema-38-migration).
|
||||
Committed
|
||||
saves/deletes/resets remain successful when manifest or GC cleanup fails;
|
||||
failures are logged and cleanup is retried on later access or reopen.
|
||||
- Storage implementation handoff reports 135/135 focused tests passed across
|
||||
|
||||
@@ -175,12 +175,16 @@ Already converted entries are reconciled on retry. Matching body revisions do
|
||||
not require rehydrating binary payloads. Direct `AssistantDatabase.initialize`
|
||||
callers and tests also run storage repair.
|
||||
|
||||
Space reclamation is separate from conversion. Every worker attempt checks
|
||||
`PRAGMA freelist_count` and runs checkpoint/VACUUM when free pages remain.
|
||||
Cancellation after the last converted entry or a VACUUM failure therefore
|
||||
allows reclamation to retry on the next startup, even with no legacy rows left.
|
||||
The schema marker alone does not establish that conversion and reclamation
|
||||
finished. General migration requirements are in the
|
||||
Space reclamation is separate from structure upgrades and conversion. Startup
|
||||
detects legacy note payloads and retains the conversion/reclamation decision in
|
||||
memory for that startup's worker retries. Only a required legacy conversion
|
||||
permits checking `PRAGMA freelist_count` and running VACUUM. A structure-only
|
||||
upgrade does not repair notes in the worker or reclaim ordinary free pages.
|
||||
Cancellation after conversion or a VACUUM failure can retry reclamation within
|
||||
the same startup. After quitting, startup checks the schema and remaining legacy
|
||||
payloads again; free pages alone do not trigger another upgrade, and SQLite can
|
||||
reuse them. No additional persistent migration marker is stored.
|
||||
General migration requirements are in the
|
||||
[database migration guide](../../development/database-migrations.md).
|
||||
|
||||
## Backup and Restore
|
||||
|
||||
@@ -7,6 +7,25 @@
|
||||
修正复发缺陷时,优先建立一条能覆盖整类问题的机制(共享选择器、共享函数、能发现全部实例
|
||||
的测试),而不是逐个点名修补。逐个修补正是这些条目反复出现的原因。
|
||||
|
||||
## 普通结构升级再次触发历史回收
|
||||
|
||||
**症状**:历史已经转换完成,后续升级仍显示清理历史并执行耗时的全库回收。
|
||||
|
||||
**原因**:只修复启动检测不够;普通旧 schema 仍会进入升级 Worker,若共享升级方法
|
||||
把任意空闲页当作回收依据,就会在以后每次结构升级时重现。
|
||||
|
||||
**规则**:共享启动决定分别描述内容转换与回收,普通结构升级默认不回收。只有具体
|
||||
旧内容转换允许回收,同次启动重试沿用内存决定;下一次启动重新检测,不增加持久标记。
|
||||
具体规则见[迁移指南](../development/database-migrations.md)。
|
||||
|
||||
**防复发**:schema 45/46 的真实 SQLite fixture 保留正常写入产生的空闲页,断言
|
||||
无历史转换、无 `VACUUM`、内容与完整性保留;真实 Worker 验证取消及重试,
|
||||
启动调度测试分别覆盖允许和禁止回收的决定在重试中不变。
|
||||
|
||||
**历史**:`8dce1d7` 和 `38d0923` 先后修正启动检测,后者随 0.13.9 发布;
|
||||
2026-09-26 检查 0.13.15 的 schema 46→47 路径发现共享 Worker 仍无条件按空闲页回收。
|
||||
本次修复在共享操作层分离结构升级与回收,而非给 46→47 增加特例。
|
||||
|
||||
## 异步界面测试只等待部分状态
|
||||
|
||||
**症状**:本地通过的界面测试在完整 CI 中找不到预期控件,或在前一步更新后立即读取到旧状态。
|
||||
@@ -15,11 +34,15 @@
|
||||
按请求完成和最终可见状态同步,不以另一个元素出现或消失代替完整操作结束。
|
||||
测试请求重试时显式控制后一次响应,分别断言等待中和响应完成后的状态;
|
||||
同一异步操作的多个最终断言放入同一个 `waitFor`。不要只延长全局超时或放宽断言。
|
||||
Electron 原生拖拽测试应确认窗口焦点、拖拽状态建立、宽度更新及拖拽结束后再断言;
|
||||
移动事件明确携带左键按下状态,不用固定睡眠代替输入处理完成。
|
||||
|
||||
**历史**:2026-09-26 检查 Desktop 0.13.14 标签 CI `36165805814` 与
|
||||
main CI `36164698647`:原生能力重试用例在启动状态未稳定时打开选项,
|
||||
浏览器关闭用例只等待页签消失便同步断言错误已清除。两处测试改用上述已有同步机制;
|
||||
是否消除 CI 失败以修复提交的 Linux CI 结果为准。
|
||||
同日 Windows 全量测试及独立复跑中,Magic Notes 原生拖拽分别停在索引列 168 和
|
||||
AI 列 280。测试同步原生输入与实际状态后独立复跑通过,保留原有宽度和持久化断言。
|
||||
|
||||
## 整窗遮罩与原生拖动区域
|
||||
|
||||
|
||||
Reference in New Issue
Block a user