tsdb-cj 的四次静默失效:代码能跑、测试能过,但什么都没发生
内容提要
文章总结 tsdb-cj 审计发现的四类静默失效:新格式桶丢失 TTL 参数导致数据永不过期;清理仅扫描 b: 前缀,遗留 k: 幽灵引用行;标签拼接 key 未校验冒号,造成查询串数据;用常量推算周一仅周四成立,窗口全等匹配落空。作者提出三条纪律:新分支复刻旧形参、多前缀删除集中管理、拼接 key 加边界守卫,并强调须验证参数真正生效而非仅调用成功。
延伸解读
静默失效为何比崩溃更昂贵
文章指出,静默失效的代价在于发现晚、定位慢且会伪装成其他问题。例如 TTL 不生效看起来像过期依赖没触发,实际是参数被丢弃。这类问题没有报错信息指向根因,只能靠审计或生产环境的数据增长告警暴露。读者应意识到,测试全绿和日志无错并不等于功能生效,尤其当验证依赖时间流逝或存储体积变化时,常规单元测试容易留下空白。
多前缀实体的删除纪律
tsdb-cj 将桶记录与引用行分别存放在 b: 和 k: 两个前缀下,清理代码只扫 b: 前缀,导致 k: 下的引用行成为永远读不到的幽灵行。后果包括存储只增不减、引用计数虚高,以及同 key 重新物化时新旧引用行叠加。文章建议把前缀定义收敛到一处,清理函数只依赖该处,而不是在两个地方各写一遍字符串。这对设计任何多 key 前缀实体的读者都有直接参考价值。
拼接 key 的边界守卫与验证方法
标签索引 key 直接拼接 tag 和 value,当值中含冒号时,不同标签组合会拼出相同前缀,导致查询串数据。修法是在 append 和 appendBatch 两个入口统一校验并拒绝含冒号的输入,且校验须在取锁与事务之前,避免失败落半个批次或占写锁。文章进一步强调,验证应关注参数是否真正生效,而非函数是否调用成功,例如设 TTL 后等它过期确认读不到,清空系列后查存储体积确认归零。
时间逻辑不能只在一个时刻验证
用常量推算周一(当天零点减 3 天)只在周四成立,其他日子会偏差最多 96 小时。由于摘要取回采用窗口全等匹配而非区间重叠,差一点就会直接落空返回无摘要,且错误形态是查不到,容易让人误以为总结没跑。文章建议改用日历对齐能力,并强调涉及周/月对齐的代码至少要覆盖 7 天里每一天的输入。只在一个时刻验证过的时间逻辑,等于没验证。
Q&A
tsdb-cj 审计中发现的静默失效是什么意思?
静默失效指代码能运行、测试能通过、日志无错误,但功能实际未生效。例如调用方传了参数、服务返回成功,但参数从未被使用,导致功能缺失且难以发现。
为什么新格式桶的 TTL 参数会被忽略?
新格式桶分支是后来添加的,编写时直接调用 txn.set(...) 而忘记使用 ttl 形参。由于编译器不报错且没有测试覆盖“新格式桶+TTL”组合,导致 TTL 从未生效,数据永不过期。
clearSeries() 清理后为什么存储体积没有下降?
因为新格式桶的桶记录(b: 前缀)和引用行(k: 前缀)存储在两个独立前缀下,而清理代码只扫描了 b: 前缀,k: 前缀下的引用行成为幽灵行,导致存储只增不减、引用计数虚高。
标签索引 key 拼接时冒号会导致什么问题?
标签索引 key 直接拼接 tag 和 value,若 tag 或 value 包含冒号,不同标签组合可能拼出相同前缀,导致查询时返回错误数据(如 filter(tag="a", value="b:c") 会混入 tag="a:b" 的数据)。修法是在入口校验并拒绝含冒号的标签。
用常量推算周一为什么会导致窗口匹配失败?
代码用“当天零点减 3 天”计算周一,但该公式仅在周四成立。其他日期会偏差 96 小时,而摘要取回使用窗口全等匹配,差一点就会完全落空,返回“无摘要”。
如何验证功能参数真正生效而不是仅仅调用成功?
应验证实际效果而非调用结果:设 TTL 后等待过期确认读不到;清空系列后检查存储体积和引用计数归零;写标签时用含冒号的歧义输入反向验证不会串数据。