跳转到主要内容

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_activitypg_stat_replicationpg_stat_replication_slotspg_stat_walpg_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

观察能力只有在问题发生前存在、事件中可访问、事件后可重放时,才是一项控制。


上一节:从时间线建立因果链 · 返回本章目录 · 下一节:把行动项变成控制 · 查看全书目录 · 查看索引中心