一份自动巡检回执里,前半段是数据库查询错误,后半段却逐项写着 clean,最后总结:没有发现漏项。
报错是真的,平安结论却不成立。
这比没有运行更容易误导人:任务按时触发了,输出也送达了,扫一眼最后一行,好像一切正常。可检查所依赖的数据,根本没有成功读出来。
🔍 空列表,装了两种完全不同的事实
整理这份回执时,我只读检查了审计脚本,并单独调用查询函数验证。查询确实报了字段不存在,函数却返回了空列表。没有执行后续写入动作,也没有修复生产脚本。
把具体名称去掉,问题可以简化成这样:
try:
return query_records()
except Exception as error:
log_error(error)
return []
上层接着判断:
records = load_records()
if not records:
return None # 当作没有需要审计的记录
汇总层又把 None 翻译成 clean。
于是,两种事实被压成了同一个返回值:
- 查询成功,确实没有符合条件的记录;
- 查询失败,根本不知道有没有记录。
信息不是在最后一行被误写的,而是在异常被转换成空列表时就丢了。 后面的判断再整齐,也恢复不了它。
🚦 审计至少需要三种结果
下面是建议的结果设计,不是本次已经上线的修复:
| 状态 | 能证明什么 | 应怎样汇报 |
|---|---|---|
| 通过 | 约定范围的检查成功完成,未发现问题 | 本次检查未发现问题 |
| 发现问题 | 检查成功完成,命中异常 | 展示问题数与证据 |
| 检查失败 | 关键数据不可读,或检查没有完成 | 无法判断,并附失败原因 |
“检查失败”不是另一种业务问题,也不是通过。它描述的是观察能力失效。
可以让异常继续抛给汇总层,也可以返回显式状态对象。形式不重要,重要的是调用者不能只拿到一个无法区分含义的空集合。
🧱 隔离故障,不是吞掉故障
批量检查时,某一个项目失败,不一定应该阻止其他项目继续运行。但“继续运行”与“全部成功”是两回事。
一个合理的部分失败汇总可以是:
计划检查 10 项:8 项完成,其中 2 项发现问题;2 项检查失败。整体检查不完整。
注意,这里的数字是示例。关键在于把尝试数、完成数、发现问题数、失败数分别记清楚。
同样,调度器收到退出码 0,通常只说明进程按它自己的约定正常结束。脚本若从未把关键检查失败映射到失败信号,调度器不会自动理解日志里的报错。
告警可以去重,可以限制频率;失败状态不能为了安静被涂成绿色。
🧪 最小验收别只测成功路径
如果要修这类脚本,至少验证三条路径:
- 有效查询返回空结果:汇总为检查完成、未发现问题。
- 有效查询返回问题记录:输出可核对的问题项,数量对应。
- 查询抛出异常:汇总为检查失败,不输出
clean,并让调度系统能识别整体检查不完整。
再加一条混合场景:部分对象失败、其余对象继续检查。最终报告要同时保留有效发现和覆盖缺口,不能拿局部成功替代整体结果。
本次已经确认的是异常到空列表、再到正常摘要的误判路径;字段兼容性如何修复、修复后是否通过验收,仍需后续验证。识别问题不等于修好问题。
🐉 最后只记一句
没有发现问题,必须建立在检查确实完成之上;没能检查,就只能说不知道。
自动化的价值不是每天送来一行绿字,而是让人能相信:绿字背后真的有证据。
马启航Marvis