跳至内容
34.8 实战:同一症状、两种成因

34.8 实战:同一症状、两种成因

本实验不给操作者“连接风暴实验”和“WAL 实验”两个有答案的按钮。runner 随机安排 两种根因、生成无语义 case ID,classifier 只能看证据:

common alert: postgresql-resource-headroom-at-risk

case opaque-A:
  connection / retention / engine / filesystem observations

case opaque-B:
  connection / retention / engine / filesystem observations

hidden answer 在分类完成后才用于验收。这样练习的是判型,不是背剧本顺序。

34.8.1 随机注入连接风暴或 WAL 保留

先读实验合同

先做不连接远端的静态检查:

static/labs/ch34/task.sh lint

只读现场采集:

export PG36_EVIDENCE_DIR="$(
  mktemp -d "${TMPDIR:-/tmp}/pg36-ch34-capture.XXXXXX"
)"
static/labs/ch34/task.sh capture

它读取 managed pg-test 的 Patroni 成员、primary system identifier/timeline、连接与 replication slot 投影,并确认 pg-test-3 没有残留实验 root。它不执行 SQL 写入、 cancel、slot、service、route 或 DCS 变更。

完整演练 guard

export PG36_EVIDENCE_DIR="$(
  mktemp -d "${TMPDIR:-/tmp}/pg36-ch34.XXXXXX"
)"
export PG36_CH34_TARGET=pg36-l2-vagrant/pg-test
export PG36_CH34_NONPRODUCTION=true
export PG36_CH34_PRODUCTION_DATA=false
export PG36_CH34_PRODUCTION_TRAFFIC=false
export PG36_CH34_CONFIRM=BLIND_FLOW_VS_RETENTION_CH34

static/labs/ch34/task.sh drill:overload

任一 guard 不符,runner 在创建远端目录前退出。evidence directory 已包含文件也会被 拒绝,避免混合两次 run。

隔离引擎

在线 fault 不发生在 managed PostgreSQL,而是在 pg-test-3 创建:

/tmp/pg36-ch34-overload-<uuid>/
  .pg36-ch34-owned
  data/
  socket/
  postgres.log

关键限制:

PostgreSQL 18.4
listen_addresses=''
Unix socket mode 0700
port 55444 only names the private socket
max_connections=24
superuser_reserved_connections=3
max_replication_slots=4
checksums=on
not a Patroni/DCS/HAProxy/PgBouncer member

实验不注入 OOM、不填满文件系统、不 drop cache,也不执行错误动作。无论正常或失败, 只在 marker 与 exact UUID root 匹配后停止临时 postmaster 并删除整个一次性目录。

两个真实 fault

FLOW:

30 non-superuser psql clients
same advisory lock
one holder sleeps for 20 seconds
other admitted clients wait on Lock
excess clients hit the connection boundary

RETENTION:

one exact inactive physical replication slot
immediately reserved restart_lsn
bounded WAL generation
stop after retained >= 32 MiB
hard cap 128 MiB

max_slot_wal_keep_size=-1 只在这个一次性实例中用于稳定展示保留机制,绝不是生产推荐 值。

34.8.2 在不知道答案时先判型,再选择动作

classifier 唯一允许读取的字段

{
  "case_id": "opaque",
  "observed_at": "UTC",
  "connection": {
    "observed_sessions": 0,
    "connection_rejections": 0,
    "lock_waiters": 0
  },
  "retention": {
    "inactive_physical_slots": 0,
    "retained_wal_bytes": 0
  },
  "engine": {},
  "filesystem": {}
}

判定合同:

FLOW =
  observed_sessions >= 18
  AND connection_rejections >= 1
  AND lock_waiters >= 1
  AND inactive_physical_slots = 0

RETENTION =
  inactive_physical_slots = 1
  AND retained_wal_bytes >= 33554432
  AND connection_rejections = 0

both or neither =
  STOP_AND_INVESTIGATE

classifier 不读取 hidden-answers.json。validator 会比较 case identity set、字段完整性、 classifier provenance 与 hidden answer;blind packet 若带 truthexpected_route 字段反而判失败。

正式 run

公开证据: overload-run.json

run id         1ae188cd-e7f2-4724-98d8-fe166cf4e33f
random order   RETENTION -> FLOW

WAL case 先出现,但 classifier 没把“第一个”解释成 flow:

inactive physical slots     1
retained WAL       42,611,296 bytes
connection rejects          0
route        PRESERVE_RETENTION_EVIDENCE

connection case:

attempted clients           30
observed sessions           21
connection rejects           9
lock waiters                20
inactive physical slots      0
route           RELIEVE_FLOW_PRESSURE

24 个 max_connections 减去 3 个 superuser reserved slots,正好留下 21 个普通 admission; 其余 9 个被拒绝。20 个 lock waiter 加 1 个持锁/睡眠 session 又构成 21 个已进入会话。 这是该临时配置下的可解释闭环,不应外推为任何生产集群连接预算。

34.8.3 对流量型恢复服务,对保留型完成安全路由

FLOW 动作

runner 用 run-specific application prefix:

pg36-ch34-flow-<run-prefix>-<client-number>

只对匹配 prefix 的 backend 执行 pg_cancel_backend。正式结果:

cancel signals sent                    21
broad cancel used                   false
max_connections changed             false
fallback client terminate/kill        0/0
post fixture sessions                   0
post SQL probe                          1

若 backend 没在 deadline 内退出,唯一 fallback 是 runner 直接持有的 exact child process; 不会扫描或终止主机上的其他 psql,更不会触碰 managed pg-test

生产映射不是“按 prefix 杀 21 个会话”,而是:

identify admission class and owner
stop producer/retry
preserve admin headroom
cancel exact expired/low-value work
verify queue and useful completion

RETENTION 动作

runner 先保存:

slot_name/type/active
restart_lsn
retained_wal_bytes
wal_status/safe_wal_size/invalidation_reason
filesystem projection

然后只 drop 带本 run identity 的 disposable slot:

scope                     exact-owned-disposable-slot
evidence preserved        before action
manual pg_wal deletion    false
post physical slots       0
post SQL probe            1

生产上的 inactive slot 没有这份授权。对应动作是联系 owner、确认 consumer/backup/RPO, 恢复消费或建立新的恢复起点,再由明确负责人批准 exact release。

managed 边界复核

before/after 都要求:

pg-test-1 primary running
pg-test-2/3 replica streaming
system identifier unchanged
timeline 19 unchanged
chapter fixture sessions 0
matching disposable roots []

公开报告因此写的是 managed mutations=0,而不是声称在 Pigsty managed cluster 上 验证了饱和行为。

34.8.4 输出动作时间线、误判代价与容量改进项

evidence bundle

before.json
exercise/
  run-manifest.json
  source-manifest.json
  exercise-evidence.json
  blind-packets.json
  hidden-answers.json
  cleanup.json
classification.json
after.json
validation-report.json
negative-report.json
public-summary.json
review.txt

已有完整证据包可重复做只读校验:

export PG36_EVIDENCE_DIR=/absolute/evidence/ch34/run
static/labs/ch34/task.sh all

validator 将 12 个实验 source file 与 SHA-256 绑定,并实际构造 34 个 mutant。反例覆盖:

production/managed mutation guard opened
scenario or classifier contract weakened
blind packet leaks truth or misses fields
classification duplicated/unsupported/wrong
flow evidence below threshold or broad cancel
retention slot/bytes/active state falsified
manual WAL deletion or leftover slot claimed
cleanup root left behind
production gate falsely approved

34 个都必须被拒绝;声明有 34 条 JSON 并不等于做了对抗验证。

误判代价

误判结果
flow 当 retention不限制 admission,连接失败与队列继续
retention 当 flowcancel session 不推进 restart_lsn,WAL 继续增长
unknown 强判 flow可能 terminate 关键/大事务并制造回滚峰值
unknown 强判 retention可能 drop 有效 slot、破坏 consumer/RPO
手工删 pg_wal破坏 crash recovery、复制、备份与现场证据

因此 unknown route 不是实验的“第三种错误答案”,而是证据不足时唯一正确的动作类别。

从实验回写容量控制

这次结果至少导出四个可验证改进:

  1. 为普通连接显式保留 admin/reserved budget,并让 pool 上限小于硬边界;
  2. 对 application pool 总和、retry 与启动 prewarm 做全局预算;
  3. 对每个 slot 记录 owner、active、restart/catalog xmin、retained bytes 与推进率;
  4. 告警同时包含“当前余量”和“增长率/耗尽时间”,并带 FLOW/RETENTION 判型链接。

不能从这次实验导出:

production max_connections should be 24
every inactive slot may be dropped after 32 MiB
42 MB WAL implies a disk incident
exact cancel always has zero fallback
managed Pigsty can tolerate the same storm

最终门禁保持:

production_ch34_gate=pending

上一节:平台级流量控制与证据 · 返回本章目录 · 下一章:数据抢救与工程取证——起死回生 · 查看全书目录 · 查看索引中心

最后更新于