v2.34-02 静默截断:10,000 个会话与 500 条消息是怎么被发现的
做备份规划时,最容易忽略的不是加密算法,而是「你以为备份了,其实没备份全」。
这一版开工前盘点导出路径,发现两个硬上限:
- 会话最多导出 10,000 个;
- 每个会话最多导出 500 条消息。
它们没有报错,没有警告,manifest 也不记录真实条数。用户看到的是一份「成功」的归档,实际上少了数据。
一、上限是怎么来的
最初的上限是列表接口的默认值。
会话列表用于界面展示,默认返回最近 100 条;消息列表用于对话渲染,默认 200 条。备份逻辑复用了这些接口,只是把参数调大,调到了 10,000 与 500。
问题在于:列表接口的语义是「取前 N 条」,不是「取全部」。
备份需要的是后者。
二、为什么静默最危险
如果导出直接报错「会话过多」,用户至少知道有问题。
静默截断的问题是:
- 归档能打开;
- 预检能通过;
- 恢复也显示成功;
- 但历史消息少了一部分。
等到用户真正需要那份旧消息时,才发现备份里没有。
这类问题不会在测试里自然暴露,因为测试数据通常只有几条。
三、修复思路:全量读取,而不是调大上限
修复没有继续调大数字,而是新增「全量读取」的仓储方法:
text
listAll() // 全部会话,按创建时间排序
listAllByConversation(convId) // 单会话全部消息,按创建时间与 rowid 排序
备份路径只调用这两个方法,不再复用列表接口。
列表接口保留原有分页语义,界面不受影响。
四、排序为什么重要
全量读取必须稳定排序。
- 会话按
created_at, id排序; - 消息按
created_at, rowid排序。
rowid 用于处理同一毫秒内写入的多条消息。没有它,恢复顺序可能在不同机器上不一致。
五、manifest 记录真实条数
只改读取还不够,还要让核对能发现问题。
manifest v9 在 conversations 轨增加 messageCount,记录实际导出的消息条数。
恢复核对时:
- v9 归档按「会话 + 消息 + 分支 + 分支事件」比对;
- v8 归档缺少消息数,回退为只比会话数。
这样新归档的差异能被看见,旧归档也不会出现假差异。
六、上限并没有消失
归档仍然有 500MB 上限。
区别是:超过上限会直接失败,不产出半份文件。用户会看到「归档过大,请分批导出」,而不是拿到一份不完整的归档。
上限从「静默丢弃」变成「显式拒绝」,这是这一版最重要的行为变化。
七、测试怎么覆盖
新增用例直接构造 501 条消息:
- 导出;
- 解包读取
conversations.json; - 断言消息数组长度为 501;
- 断言 manifest 的
messageCount为 501。
这个用例很朴素,但它能防止上限被重新引入。
八、一条经验
备份路径不要复用面向界面的列表接口。
界面接口的默认值、分页与排序都是为展示服务的;备份需要的是「全部、稳定、可核对」。
当两者复用时,上限会以「默认值」的形式悄悄进入备份,而且不会报错。