第 12 / 21 篇

第11章 删除、墓碑与旧事件重放

“搜索不到了”只是删除的一个表面结果。若同步队列里还保留旧内容事件,系统可能在下一次重试时把文档重新写回来。若缓存命中仍能摘录,用户甚至不用再搜索。一个明确的删除协议,必须同时回答这两个问题。

删除也推进版本

随书 delete() 要求指定比当前内容版本更大的整数。它在写事务中清除片段、FTS 行和 ACL,把正文置空,标记 deleted=1,并推进内容与权限版本。文档身份和墓碑仍然保留,因此系统知道这个编号已经被主动删除。

保留墓碑意味着旧内容的 revision=1 再次到达时,不会被当成全新文档。即使使用更高版本,默认更新也会得到 deleted_document_requires_explicit_restore。恢复必须明确传入 restore=True,并提供新的显式 ACL,让“恢复内容”和“重新授权”成为一个有意识的动作。

这个教学接口没有实现删除事件的重复幂等确认;再次使用相同删除版本会被拒绝,因为它要求新版本。连接器可以记录事件处理结果来处理重复投递,或者设计完整的删除事件身份,但不能把接口报错随意当成删除成功。

从三个方向检查删除

第一步,在删除前保存目标文档命中;删除后交给 answer(),应该无法再形成证据。第二步,执行新搜索,确认返回列表没有目标编号;单查正文标记不够,因为重复的 FTS 行或旧向量仍可能存在。第三步,重放旧版本内容,确认它被墓碑挡住。

演示用年假文档完成这组操作:先检索并保留 leave 的命中,随后以第二版删除,旧证据拒绝、新搜索没有该文档,重放第一版得到 stale_or_conflicting_revision。测试还覆盖较高版本未明确恢复时仍被拒绝,以及明确恢复后使用新授权列表。

如果同一查询还匹配其他合法文档,检索可以继续返回那些文档。删除承诺针对目标身份与内容,不要求所有带有共同词语的查询都变成空结果。对实验断言保持这种精度,可以避免把合法检索行为误当成删除失败。

逻辑不可见不是物理擦除

本书的保证是当前活动索引不再返回正文或引用。SQLite 数据页、日志、备份、源系统、观察日志和已经交付的回答不在这个保证内。墓碑还保留标题、身份和摘要等维护信息,不能声称删除后所有元数据消失。

若业务要求物理擦除,需要另外定义保留策略、备份淘汰、密钥生命周期、审计记录与验证方法。简单执行 SQL DELETE 不等于完成所有副本的销毁,本书也没有进行磁盘取证或备份清除实验。

墓碑保留多久

只要旧事件仍可能出现,丢弃墓碑就可能失去防重放信息。实际系统应把源事件保留上限、同步水位、备份恢复窗口和墓碑生命周期联系起来。不能仅以“最近没人搜索”为依据删除墓碑。

删除因此属于索引生命周期的一等状态。它不是一次清理脚本,而是与版本顺序、恢复授权和引用复核共同工作的协议。下一章讨论内容没有删除、权限却改变时,应怎样执行相同的失效原则。