36.3 证据质量与决策复盘
复盘拥有响应时没有的时间、权限和上下文,因此最容易犯 hindsight bias:把后来才知道 的答案投射给当时的人。决策质量必须按当时可用信息评估,结果质量则按后来完整 证据评估。
36.3.1 哪些事实当时可见,哪些后来才知道
建 knowledge timeline
普通 timeline 记录系统事件;knowledge timeline 记录响应者何时获得什么信息:
knowledge_id: K-...
observed_at: ...
available_to_roles: [...]
statement: ...
kind: observation | inference | report
source:
system: ...
object_identity: ...
query_or_artifact_hash: ...
quality:
directness: direct | derived | hearsay
completeness: ...
clock_uncertainty_ms: ...
interpretation_at_the_time: ...
decision_ids_informed: [...]
later_evidence: ...
correction: ...后来发现原告警是 replica、不是 primary,不要覆盖旧记录。追加:
T1 observed: endpoint E reports recovery=true
T1 inference: E is believed to be current primary
T2 correction: inventory and DCS show E was a replica这样才能问:“在 T1,接受写流量是否合理?”而不是用 T2 的答案责怪 T1。
四栏复盘表
| 当时可见事实 | 当时假设 | 后来证据 | 当前结论 |
|---|---|---|---|
| client timeout 上升 | primary 过载 | proxy backend errors + DB 正常 | 故障在 route |
| WAL 目录增长 | WAL 生成太快 | slot restart LSN 不动 | retention owner 阻塞 |
| 新节点可写 | failover 成功 | 旧主仍可接受直连 | authority 尚未安全 |
| checksum clean | 数据无损坏 | 业务 digest 漂移 | page 完整性与语义不同 |
要求每个结论能回指源证据,并明确 source identity。截图可帮助人理解,但通常缺查询、 时间窗、变量、完整返回和 hash;关键结论保留机器可读原始投影。
决策日志与复盘文档分离
响应期间的 decision record 应 append-only:
decision_id: D-...
at: ...
objective: ...
known_evidence: [K-...]
hypotheses_considered: [...]
chosen_action:
expected_observation:
stop_condition:
rollback:
authority:复盘可以评价该决定,却不能重写当时输入。若事故中没有决策日志,这本身就是一个证据 缺口;不要靠会后记忆补成精确逐分钟事实。访谈内容标记为 recollection,并与日志、 审计、metric 和 trace 交叉验证。
证据保真与最小披露
raw evidence 可能包含 query text、角色、IP、token、客户标识或 payload。采用两层:
restricted original
immutable/retained/access-audited
review projection
redacted, source-bound, sufficient for the claim去敏不应破坏关联键;可用 stable pseudonymous token、区间、计数与 hash。复盘仓库只放 projection 和原件位置/权限,不复制 secret 或个人数据。
36.3.2 哪些假设被验证,哪些动作靠运气
好结果不能证明好决策
四种组合都值得复盘:
| 决策过程 | 结果 | 判断 |
|---|---|---|
| 证据充分、边界清楚 | 成功 | 机制候选,仍需复验 |
| 证据充分、边界清楚 | 失败 | 模型或实现有缺口 |
| 猜测、无 stop/rollback | 成功 | near miss / luck |
| 猜测、无 stop/rollback | 失败 | 显性事故 |
“重启后好了”只证明重启与恢复同时发生。它可能清掉等待队列、终止事务、触发 failover、 刷新 cache 或碰巧等到下游恢复;没有前后证据就无法选出机制,也无法知道丢了什么。
为每个关键动作做机制审计
preconditions
执行前必须为真的事实是否被验证?
scope
作用到 exact object/session/node/cluster 吗?
mechanism
为什么它应改变目标症状?
expected
多久、在哪个观察面看到什么?
stop / rollback
哪个信号表明应停止或撤回?
result
预期与实际是否一致?
repeatability
在隔离环境重演后仍成立吗?例如取消 exact application_name 会话后 lock waiters 清零,可以支持“这些 fixture
session 构成 flow pressure”;它不能证明任意高连接事故都应取消会话。又如从
known-good snapshot 恢复后业务 digest 匹配,支持该 fixture 恢复路线;它不能证明
生产 backup 覆盖同一范围。
验证假设,而不是验证故事
每个 hypothesis 写:
hypothesis: H-...
prediction:
if_true: ...
if_false: ...
test:
isolation: ...
changed_variable: ...
independent_observations: [...]
result:
alternative_explanations:
status: supported | weakened | rejected | unresolved如果测试同时改变配置、版本、流量和拓扑,即使问题消失也无法归因。生产不能安全复现 时,在 clone、replay、模型或历史数据上验证,并把外推边界写清。
near miss 也进入控制系统
这些情况值得和事故一样记录:
- 错命令被 safeguard 阻止;
- 误切换前发现 system identifier 不符;
- dangerous parameter 在唯一副本执行前被 review 拒绝;
- restore candidate 偶然正确,但 target 没有审计来源;
- unknown request outcome 恰好没有重复副作用。
near miss 提供了低损失的失效路径证据。若只统计造成用户影响的事件,平台会忽略已经 穿透多层防线、仅靠最后运气没有出事的路径。
36.3.3 告警、日志和时间同步缺口
从问题反推 observation contract
不要以“多收日志”为默认行动。先列事故中无法及时回答的问题:
| 问题 | 所需信号 | identity/维度 | retention |
|---|---|---|---|
| 谁受影响 | user SLI / request outcome | service、operation、segment | 至少覆盖 SLO 窗口 |
| 查询为何慢 | wait、plan、query ID、I/O | cluster、db、role、query | 覆盖发布与周期负载 |
| 谁保留 WAL | slot/subscriber/archive | system id、timeline、slot owner | 覆盖恢复窗口 |
| 谁拥有写权威 | DCS、Patroni、timeline、route | cluster/member/endpoint | 覆盖 failover 前后 |
| 数据何时变化 | audit/WAL/business event | transaction/token/object | 由 RPO、合规决定 |
| 恢复是否可信 | backup/restore/business manifest | source/cutoff/candidate | 覆盖证据保留期 |
每个信号写 producer、collector、query、labels、刷新周期、缺失语义、owner、retention、 权限与成本。missing value 不能默认解释成 zero/healthy。
PostgreSQL 证据的时效与局限
PostgreSQL cumulative statistics、pg_stat_activity、pg_stat_replication、
pg_stat_replication_slots、pg_stat_wal、pg_stat_io 等提供不同观察面,但要记录:
- counter 是累计值还是当前 gauge;
- stats reset、server restart 和 failover 是否改变基线;
- 读取 snapshot、事务和刷新延迟;
- query text 是否截断或因权限不可见;
- standby 与 primary 的语义差异;
- extension/版本是否改变列和统计;
NULL、空行与零值分别意味着什么。
日志应使用可解析格式(如 csvlog/jsonlog)和稳定关联字段,但 log_statement=all
可能暴露敏感数据并产生高开销。优先记录必要 identity、duration、SQLSTATE、query ID、
application/client context;query 参数和 payload 按数据分类处理。
指标、日志与告警要能互相落点
一条 page 应先表达 user impact 或 error-budget threat,再链接诊断上下文:
alert
exact service + SLI window + burn/severity
-> dashboard
user outcome + dependency + PostgreSQL/host
-> runbook
evidence requests + route predicates + stop line
-> raw source
reproducible query/log projection按每个 instance 发 30 条 alert 通常不如按 service impact 聚合一次,再保留 instance 分解。告警规则也需要单元/合成 time series 测试:正常、阈值边界、缺失数据、抖动和 长期低速 burn 都要覆盖。
时间同步是证据基础设施
至少监控:
clock source and synchronization state
offset / frequency error
last successful sync
host suspend/resume or VM migration
timezone configuration
collector ingestion delay数据库 now() 是事务开始时间;statement_timestamp() 与
clock_timestamp() 语义不同。应用、PostgreSQL log、systemd journal、proxy 和
monitoring timestamp 还可能分别来自 event time 与 ingest time。复盘合并前先统一
UTC 展示、保留原时区,并标注误差;不能靠肉眼把相近 timestamp 当因果顺序。
缺口行动要可验收
不要写:
增加更多监控。
完善日志。
保证时间准确。写成:
owner: observability-platform
artifact: pg36_service user-SLI rule revision 3
test: synthetic 5% failure over declared windows
pass: fast window pages; slow burn records ticket; zero/missing remain distinguishable
clock test: inject collector offset fixture and reject order claims below uncertainty
evidence: rule test output + notification trace + dashboard link
revalidate: 30 days观察能力只有在问题发生前存在、事件中可访问、事件后可重放时,才是一项控制。
上一节:从时间线建立因果链 · 返回本章目录 · 下一节:把行动项变成控制 · 查看全书目录 · 查看索引中心