cmd_bd_doctor_fix_cleanup 模块技术深度文档
1. 模块概述
cmd_bd_doctor_fix_cleanup 模块是 Beads 项目中医生命令系统的核心清理组件,专门负责处理数据库中的"污染"数据和过期资源。这个模块解决了两个关键问题:
1.1 问题空间
在 Beads 的长期使用过程中,数据库会积累两类需要清理的内容:
- 过期关闭的问题:随着时间推移,许多已关闭的问题不再需要保留,但手动清理既繁琐又容易出错
- 巡逻污染数据:系统运行过程中会自动生成一些临时 beads(如巡逻摘要和会话结束记录),这些数据在完成使命后会成为"污染",占用存储空间并影响查询性能
1.2 设计洞察
这个模块的核心设计思想是选择性自动化清理——它不是盲目地删除数据,而是通过可配置的策略和明确的模式匹配来安全地移除不再需要的资源。这种方法在自动化和安全性之间取得了平衡。
2. 架构与数据流程
2.1 组件关系
此模块在系统中的组件关系如下:
- 上游组件:CLI Doctor Commands → Maintenance and Repair
- 本模块:cmd_bd_doctor_fix_cleanup
- 核心功能:
- StaleClosedIssues:处理过期关闭问题
- PatrolPollution:处理巡逻污染数据
- 下游依赖:
- configfile.Load:加载配置
- dolt.NewFromConfig:创建存储连接
- store.SearchIssues:查询问题
- store.DeleteIssue:删除问题
2.2 核心数据流程
- 输入验证阶段:所有清理操作首先验证当前路径是否为有效的 Beads 工作区
- 配置加载阶段:从
metadata.json加载配置,确定清理策略和阈值 - 存储连接阶段:通过
dolt.NewFromConfig工厂方法创建适当的存储后端连接 - 问题查询阶段:使用
store.SearchIssues查询符合清理条件的问题 - 选择性删除阶段:根据规则过滤(如跳过固定问题)并删除目标问题
- 结果报告阶段:向用户报告清理操作的统计信息
3. 核心组件深度解析
3.1 cleanupResult 结构体
type cleanupResult struct {
DeletedCount int
SkippedPinned int
}
设计意图:这个简单的结构体用于封装清理操作的结果统计。虽然在当前实现中主要用于内部逻辑组织,但它为将来可能需要返回结构化结果的 API 变化预留了空间。
3.2 StaleClosedIssues 函数
核心职责:删除超过配置天数的已关闭问题。
设计细节:
- 默认禁用策略:通过将
stale_closed_issues_days默认值设为 0,确保用户必须显式启用此功能,避免意外数据丢失 - 固定问题保护:专门检查
issue.Pinned标志,确保重要问题即使过期也不会被删除 - 后端兼容性:明确跳过 Dolt 后端,反映了当前实现对 SQLite 特定功能的依赖
参数与返回值:
path string:Beads 工作区的根路径- 返回
error:操作成功时返回 nil,失败时返回带有上下文的错误
内部流程:
- 验证工作区有效性
- 解析
.beads目录路径 - 加载配置文件
- 检查后端类型和清理阈值
- 创建存储连接
- 计算截止日期并构建过滤条件
- 执行查询并遍历结果
- 选择性删除并报告结果
3.3 PatrolPollution 函数
核心职责:清理系统自动生成的临时 beads,主要是巡逻摘要和会话结束记录。
设计细节:
- 模式匹配策略:使用字符串前缀和后缀匹配来识别污染数据,而不是依赖可能变化的内部标志
- 批量操作模式:先收集所有要删除的 ID,然后再执行删除操作,这种方法简化了错误处理和统计
- 无配置设计:与
StaleClosedIssues不同,此功能不需要用户配置,因为这些临时 beads 的清理策略是明确且无争议的
识别的污染模式:
- 巡逻摘要:标题匹配
"Digest: mol-*-patrol"模式 - 会话结束记录:标题以
"Session ended:"开头
4. 依赖关系分析
4.1 上游依赖
此模块被维护与修复模块调用,作为 doctor fix 系统的一部分执行。
4.2 下游依赖
- configfile 包:用于加载和访问配置,特别是
GetBackend()和GetStaleClosedIssuesDays()方法 - dolt 包:通过
NewFromConfig工厂方法创建存储连接,提供数据访问层 - types 包:使用
StatusClosed常量和IssueFilter结构体来构建查询
4.3 数据契约
此模块依赖几个关键的数据契约:
IssueFilter结构体必须支持Status、ClosedBefore和Ephemeral过滤条件- Issue 对象必须有
ID、Title、Pinned字段 - 存储层必须实现
SearchIssues和DeleteIssue方法
5. 设计决策与权衡
5.1 默认禁用 vs 默认启用
决策:选择默认禁用过期问题清理功能(stale_closed_issues_days=0)
理由:数据丢失是不可逆的,默认禁用提供了更高的安全性。这符合"安全默认"的设计原则,确保用户在了解后果之前不会意外删除数据。
权衡:增加了用户的配置负担,但换取了更高的数据安全性。
5.2 字符串模式匹配 vs 结构化标志
决策:在 PatrolPollution 中使用字符串模式匹配而不是结构化标志
理由:
- 这些临时 beads 的生成逻辑可能分布在代码库的不同部分
- 字符串模式是可见的、自描述的,不需要额外的文档
- 降低了与生成这些 beads 的代码的耦合度
权衡:如果标题格式发生变化,清理功能可能会失效。但这种方法提供了更好的向后兼容性和更低的耦合度。
5.3 先收集后删除 vs 即时删除
决策:在 PatrolPollution 中先收集所有要删除的 ID,然后再执行删除
理由:
- 简化了统计逻辑,可以准确报告将要删除和实际删除的数量
- 使错误处理更清晰,可以区分查询错误和删除错误
- 如果需要,可以在执行删除前添加确认步骤
权衡:对于非常大的数据集,这会消耗更多内存,但在这个使用场景下,污染数据的数量通常不会达到内存成为问题的规模。
5.4 Dolt 后端跳过
决策:明确跳过 Dolt 后端的过期问题清理
理由:当前实现依赖 SQLite 特定的功能,Dolt 后端可能有不同的事务模型或版本控制机制,需要更谨慎的处理。
权衡:这限制了功能的适用范围,但避免了在不兼容的后端上可能出现的数据一致性问题。
6. 使用指南与示例
6.1 启用过期问题清理
要启用过期关闭问题的自动清理,需要在项目的 metadata.json 中设置:
{
"stale_closed_issues_days": 90
}
这将启用删除 90 天前关闭的问题(固定问题除外)。
6.2 执行清理操作
清理操作通过 doctor 命令系统触发:
bd doctor fix
这将执行所有启用的修复操作,包括清理功能。
7. 边缘情况与注意事项
7.1 固定问题保护
即使问题已关闭且超过了配置的天数,如果 issue.Pinned 为 true,它也不会被删除。这是一个重要的安全机制,但用户有时可能会忘记他们固定了某些问题。
7.2 Dolt 后端限制
当前实现对 Dolt 后端的支持有限。在使用 Dolt 作为存储后端时,过期问题清理将被跳过,这可能会导致用户困惑。
7.3 标题格式依赖
PatrolPollution 函数依赖于特定的标题格式。如果系统其他部分更改了这些临时 beads 的标题生成方式,清理功能将无法识别它们,导致污染数据积累。
7.4 错误处理策略
删除操作采用了"尽力而为"的策略:如果某个问题删除失败,会记录警告但继续处理其他问题。这确保了单个问题的问题不会阻止整个清理过程,但也意味着可能会有部分残留数据。
8. 扩展与改进方向
8.1 可能的扩展点
- 更丰富的过滤条件:可以扩展
StaleClosedIssues以支持更多过滤条件,如标签、作者或特定的问题类型 - 预览模式:添加一个预览模式,显示将要删除的内容而不实际删除
- Dolt 后端支持:为 Dolt 后端实现兼容的清理逻辑
- 可配置的污染模式:使
PatrolPollution中的模式匹配可配置,而不是硬编码
8.2 技术债务
- cleanupResult 的使用:当前定义了
cleanupResult结构体但未充分利用,可以考虑将其用于更结构化的结果返回 - 代码重复:两个函数中有一些共同的逻辑(如工作区验证、存储连接创建),可以考虑提取到共享的辅助函数中
- 测试覆盖:目前没有看到测试代码,添加单元测试将提高模块的可靠性