From f1b504984879690e37ae96ea2af74477411707fa Mon Sep 17 00:00:00 2001 From: Ava Date: Fri, 15 May 2026 12:23:18 +0800 Subject: [PATCH 1/9] docs: chaos pod-kill vs engine-internal-crash FSFO asymmetry Add engine-neutral methodology guide for chaos test matrix design, documenting why pod-kill and internal-process-kill exercise different failover code paths and must both be covered. Add Oracle 19c case appendix consolidating round-1 (pod-kill, no FSFO, standby SUSPEND) vs round-6 (SMON SIGKILL, FSFO fires + auto-reinstate) side-by-side evidence on the same o19p15v9 cluster. --- ...pod-kill-vs-engine-internal-crash-guide.md | 136 +++++++++++ ...d-kill-vs-smon-kill-fsfo-asymmetry-case.md | 231 ++++++++++++++++++ 2 files changed, 367 insertions(+) create mode 100644 docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md create mode 100644 docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md diff --git a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md new file mode 100644 index 0000000..faff07c --- /dev/null +++ b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md @@ -0,0 +1,136 @@ +# Addon Chaos:Pod-Kill vs 引擎内部 Crash 双轴覆盖指南 + +> **Audience**: addon dev / test / TL,正在设计 chaos 测试矩阵或评估现有 chaos 覆盖完整性 +> **Status**: stable +> **Applies to**: any KB addon with HA/failover engine(MySQL / Oracle / PostgreSQL / Redis / MongoDB 等) +> **Applies to KB version**: any +> **Affected by version skew**: 不受 KB 版本影响 — 本文是 chaos test 设计原则,与 addon image 版本无关 + +## 先用白话理解这篇文档 + +很多 chaos 测试默认 "杀 pod" 就等于 "测试了节点故障"。其实远远不够。 + +同一个 DB 实例的"崩溃"有两种很不一样的形态: + +1. **K8s 层**:`kubectl delete pod --force --grace-period=0`、节点宕机、Pod evict。Pod 整个消失,K8s 重建。 +2. **引擎内部**:DB 关键后台进程崩溃(SMON / LGWR / WAL writer 等)、OOM 杀掉了一个进程、引擎内部断言 fail 自杀。Pod 还在,容器还在,但 DB 实例死了。 + +这两种故障在生产里都会出现,但它们**走完全不同的恢复路径**,因此**也可能触发完全不同的 failover 行为**。一个测了不代表测了另一个。 + +本文给出一组明确的口径,让你在 chaos 矩阵里把"K8s 层杀"和"引擎内部 crash"分两个轴显式覆盖。 + +## 适用场景 + +当你在做下面任一件事时,本文适用: + +- 设计或扩展一个新 addon 的 chaos 测试矩阵。 +- 评估 ship-readiness 时怀疑现有 chaos 覆盖偏一边。 +- 现场观察到生产里有 failover 现象,但本地 chaos 没复现 — 排查是不是测错了轴。 +- 别的 team 跑通了某种 chaos 你跑不通(或反过来)— 检查是不是两人测的不是同一个轴。 + +## 核心边界:两轴各自的恢复路径 + +### 轴 A — K8s 层 pod kill + +``` +kubectl delete pod ... --force --grace-period=0 + ↓ +Pod 立即从 API server 摘除 + ↓ +StatefulSet controller 立即 schedule 新 pod + ↓ +init container → main container 启动 → 引擎 bootstrap → 引擎主进程 ready +``` + +**特征**: +- 引擎进程被 SIGKILL(来自 kubelet 销毁容器)— 没有 PMON 清理、没有 alert log 记录"why I died" +- 网络连接对外**直接断开**(TCP RST 或 timeout)— 远端 standby / observer 看到 connection refused +- Pod 重建可能耗时数十秒到几分钟(PVC mount、image cache、bootstrap 逻辑) +- kbagent / sidecar 容器**也跟着重启** — 任何依赖 sidecar 启动期间临时做的事(如 symlink)都会重新做或丢 + +### 轴 B — 引擎内部 crash + +``` +kubectl exec -c -- kill -9 + ↓ +引擎内部检测到关键进程死,触发 instance 自我终止 + ↓ +引擎清理 + 写 alert log + ↓ +容器 PID 1 watchdog 检测引擎死,exit + ↓ +kubelet restart 容器(同一 pod 内) + ↓ +新引擎 bootstrap +``` + +**特征**: +- 引擎有时间**写 alert log** 解释为什么死 — 留下证据 +- 网络连接**可能短暂保持**(listener 进程独立存活,但服务无响应)— 远端 standby / observer 看到 ORA-1034 类的"service unavailable"而不是 connection refused +- **只有 main 容器重启**,sidecar 容器(kbagent / exporter)保持原状 — sidecar 内的 ephemeral 文件保留 +- 整体恢复通常**更快**(共享 PVC 不用重 mount、网络栈不重建) + +## 为什么 failover 行为会不同 + +Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联**: + +- 轴 A — TCP 直接断开:standby/observer 可能立刻判定"primary 不可达",但同时也判定"我已经 out-of-sync"(因为我们用 async transport 时 standby 没收到最后一秒的 redo)。某些保护模式下,standby 会进入 SUSPEND 状态以防止数据丢失 promotion。**FSFO 可能因 standby SUSPEND 而暂停**。 +- 轴 B — 引擎崩溃但 listener 还活着:standby/observer 看到 service-level 错误("instance not available")但 TCP 通路还在;standby 自己最后一次 redo apply 可能刚好接近 synced;observer 看到的是干净的"primary 死了,但 standby 还是 healthy"的状态。**FSFO 触发的几率更高**。 + +**这不是 bug 也不是 feature — 是不同故障模式天然的不同信号** — 只是 chaos test 必须把这两个轴显式测,否则你只验证了一类信号下的 failover。 + +## 测试矩阵建议 + +最小覆盖矩阵(不分引擎通用): + +| # | 轴 | 故障注入 | 期望验证 | +|---|---|---|---| +| 1 | A | primary pod kill | engine self-recovery / FSFO 行为 / 写可用性 | +| 2 | A | standby pod kill (非 failover 目标) | standby 自愈,primary 不受影响 | +| 3 | A | FSFO target pod kill | target auto-shift / 失联期间 FSFO blind window | +| 4 | A | 全部 standby 同时 kill | primary 写可用性(async/sync 模式差异) | +| 5 | A | observer (or failover decision-maker) pod kill | observer 自动 reconnect、FSFO blind window | +| 6 | **B** | **primary 内部关键进程 kill** | **engine self-restart watchdog 工作 / FSFO 行为差异(与 #1 对比)** | +| 7 | B | standby 内部关键进程 kill | standby self-restart | +| 8 | A | rolling kill across pods | 序列失败下的稳定性 | + +**核心契约**: 至少 #1 (轴 A) 和 #6 (轴 B) 都必须做,并显式比较它们的 FSFO 行为。仅做 #1 = chaos 覆盖不完整。 + +## 引擎适配 checklist + +不同引擎的"关键后台进程"不一样。设计 #6/#7 时先确认这些: + +1. **哪些进程是 critical(杀了导致整个 instance 终止)**:例如 Oracle 的 PMON / SMON / LGWR、MySQL 的 ibwriter 内核线程、PostgreSQL 的 postmaster 主进程 +2. **引擎自己写不写 crash 原因到 alert/error log**:决定测试结束后能不能取得"为什么死"的证据 +3. **容器 PID 1 是谁**:是 init system?是 entrypoint script?entrypoint script 有没有写 watchdog 检测引擎死? +4. **如果 PID 1 不死、引擎死了,容器会不会被 K8s 回收**:取决于 liveness probe 是否能检测到、failureThreshold 多少。watchdog(PID 1 主动 exit)比 liveness probe 快得多 + +## 验收口径 + +每一轮 chaos 必须留三类证据: + +1. **engine alert log**:从 kill 时间点到 recovery 完成的全段,提取关键事件("X process died" → "instance terminated" → "instance starting") +2. **broker / failover decision-maker 日志**:从 kill 时间点到 FSFO 决策完成的全段,包括 threshold 计时、target 评估、failover 执行 +3. **cluster-level state snapshot**:定时(10-30s 一次)轮询 broker / cluster config,记录 status 字符串、active target、各成员状态的轨迹 + +不取得这三类证据中的任何一类,FSFO 行为的结论都是"看着对",不是"证据上对"。 + +## 反模式 + +1. **"杀 pod 跑通了,就过了"** — 只覆盖了轴 A,FSFO 在轴 B 下的行为没测。 +2. **"OOM kill 容器 = 内部 crash"** — 错。OOM kill 把整个容器杀掉,是轴 A 类型;要测轴 B 必须 SIGKILL 引擎进程而不打容器。 +3. **"标准故障注入工具(chaos-mesh / litmus)够用了"** — 这些工具默认主要做轴 A(pod kill / network partition / node drain)。轴 B 需要 `kubectl exec ... kill -9 `,没几个 chaos 框架原生支持。 +4. **"内部 crash 测试太危险,不在 prod 跑"** — chaos 本来就是在 staging / pre-prod 跑。轴 B 的内部 crash 在 prod 里**实际上每天发生** — 只是你不知道而已(DBA 看 alert log 才能看到)。 +5. **"只测一次轴 B 就够"** — 应该把不同 critical 进程都测一遍。SMON 和 LGWR 的 fail-out 路径可能不同。 + +## 与其他 chaos / 测试方法论的关系 + +- [`addon-controller-crash-resilience-guide.md`](addon-controller-crash-resilience-guide.md) — 覆盖**控制层** crash(KB controller 在 Ops 中段挂);本文覆盖**数据层** crash。两者是正交的,都要测。 +- [`addon-test-baseline-standard-guide.md`](addon-test-baseline-standard-guide.md) — chaos 矩阵的 baseline 标准;本文是它的 chaos 部分的轴扩充。 +- [`addon-soak-test-result-classification-guide.md`](addon-soak-test-result-classification-guide.md) — 长跑型 chaos 的结果归类;本文是单次 chaos round 设计原则。 + +## 案例附录 + +引擎相关的实测命令、日志样本、recovery 时间数据放在独立案例材料里: + +- [`cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c on KubeBlocks o19p15v9 cluster: 6 轮 chaos 矩阵实测,pod-kill (round 1) 与 SMON-kill (round 6) 的 FSFO 行为对比 + 完整 alert log + observer log + broker poll 轨迹 diff --git a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md new file mode 100644 index 0000000..917ad98 --- /dev/null +++ b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md @@ -0,0 +1,231 @@ +# Oracle 19c — Pod-kill vs SMON-kill FSFO 行为不对称 案例 + +> **Audience**: Oracle addon dev / test,以及任何 HA 引擎团队想了解 chaos 注入层级差异如何改变 failover 决策 +> **Status**: stable +> **Applies to**: Oracle 19c on KubeBlocks(方法论参见 [`../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md)) +> **Applies to KB version**: 验证于 KB 1.0.3-beta.x(与具体 KB 版本无关 — 现象由 Oracle DG broker + K8s 重启路径决定) + +本案例是 [`../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) 的工程现场补充。在同一个 Oracle 19c 三节点集群(MaxPerformance + ASYNC + FSFO threshold 30s)上跑同一组配置,仅故障注入层级不同 — 一次是 K8s 层 pod-kill,一次是 Oracle 引擎内部 SMON SIGKILL — FSFO 出现了完全相反的结果。 + +## 现场 + +- **时间**:2026-05-15 +- **集群**:`o19p15v9` on idc2 vcluster +- **拓扑**:1 primary(ORCLCDB_0 / oracle-0)+ 2 standby(ORCLCDB_1 / ORCLCDB_2)+ 1 observer +- **DG 保护模式**:MaxPerformance +- **Redo transport**:ASYNC NOAFFIRM +- **FSFO**:Enabled in Potential Data Loss Mode,threshold = 30s,AutoReinstate = TRUE +- **CommunicationTimeout**:180s +- **Active Target(pre-chaos)**:round 1 时为 ORCLCDB_1;round 6 时为 ORCLCDB_1(经过 round 3/4/5 后又重新对齐) +- **Evidence path**:`/Users/wei/ApeCloud/evidence-skyworth-oracle-19c/chaos-primary-kill-2026-05-15/` 与 `chaos-smon-kill-primary-2026-05-15/` + +## Round 1 — K8s 层 pod-kill on primary + +### 故障注入 + +``` +kubectl delete pod -n oracle-test \ + o19p15v9-oracle-0-x-oracle-test-x-oracle-test \ + --grace-period=0 --force +``` + +T0 = `2026-05-15T03:07:11Z`。Force delete,pod 整个从 API server 摘除,所有容器(oracle / kbagent / config-manager / exporter)一起销毁。StatefulSet 立即调度新 pod。 + +### Observer 视角 + +| time | T+ | observer 日志 | +|---|---|---| +| 03:07:34Z | 23s | `Primary database cannot be reached.` | +| 03:07:35Z | 24s | `Standby is in the SUSPEND state. Fast-Start Failover suspended. Reset FSFO timer.` | +| 03:07:42Z | 31s | observer probes fail with `ORA-12537: TNS:connection closed during DB startup` | +| 03:08:07Z | 56s | primary(已重启)请求 `UNSYNC/LAGGING transition with ORCLCDB_1` | +| 03:08:16Z | 65s | primary 回到 `NOT LAGGING` 状态 | +| 03:09:48Z | 157s | broker `Configuration Status: SUCCESS`(status updated 0s) | + +### Broker 视角(关键 poll) + +``` +poll 1 (T+12s) ORCLCDB_0 = Primary(仍在 broker 记录里), status=SUCCESS(stale) +poll 2 (T+24s) ORCLCDB_0: Error ORA-1033 (ORACLE initialization or shutdown in progress), status=ERROR +poll 3 (T+37s) ORCLCDB_0: Error ORA-16525 (DG broker is not yet available), status=ERROR +... (primary 重启回来,重新接管原 role) ... +poll N (T+157s) Status=SUCCESS, ORCLCDB_0 仍是 Primary +``` + +### 结果 + +- **FSFO 没有触发** +- **没有发生角色切换** — 故障前 primary 是 ORCLCDB_0,故障后还是 ORCLCDB_0 +- **Standby ORCLCDB_1 进入 SUSPEND 状态**,observer 主动 reset 了 FSFO timer +- 整个流程是"primary self-recovery",不是"failover" + +### 为什么 FSFO 没触发 + +Observer 给出的原话是: + +> Standby is in the SUSPEND state. Fast-Start Failover suspended. + +这是 FSFO 的**数据丢失安全护栏**。在 MaxPerformance + ASYNC 模式下: + +1. Pod-kill 是 SIGKILL — primary 进程被 kubelet 直接杀,没有时间 flush 最后一秒 redo +2. 与此同时,TCP 连接也立即 RST — standby 看到 connection refused,redo transport 立即中断 +3. Standby 自己感知到"我手上的 redo 应该不完整",状态进入 SUSPEND +4. Observer 看到 standby = SUSPEND → 触发安全护栏:拒绝 failover 到一个可能丢数据的 standby +5. 在 standby SUSPEND 期间,每一轮 observer tick 都 **reset FSFO timer** — threshold 永远凑不齐 + +随后 primary pod 在 ~30-60s 内重启回来,broker 通过自我恢复路径完成 `UNSYNC/LAGGING → NOT LAGGING` 状态机切换,cluster 回到原 role 拓扑。 + +## Round 6 — 引擎内部 SMON SIGKILL on primary + +### 故障注入 + +``` +kubectl exec -n oracle-test o19p15v9-oracle-0 -c oracle -- \ + bash -c 'kill -9 $(pgrep -f ora_smon_ORCLCDB)' +``` + +T0 = `2026-05-15T04:09:28Z`。SMON PID = 113。**Pod 没被杀,只有 Oracle 引擎里的 SMON 后台进程被 SIGKILL**。oracle 容器的 PID 1 是 runOracle.sh(包含 watchdog 循环)。 + +### Alert log 视角(来自 oracle ctr 内部) + +| time | T+ | alert_ORCLCDB.log | +|---|---|---| +| 04:09:33.121Z | 5s | `PMON (ospid: ): terminating the instance due to ORA error` | +| 04:09:33.122Z | 5s | `Cause - 'Instance is being terminated due to fatal process death (pid: 24, ospid: 113, SMON)'` | +| 04:09:33.125Z | 5s | `System State dumped to trace file ORCLCDB_diag_90.trc` | +| 04:09:35.637Z | 7s | `Instance terminated by PMON, pid = 67` | +| 04:10:09.511Z | 41s | `Starting ORACLE instance (normal) (OS id: 31)` ← 新容器内的 runOracle 启动新 instance | + +> **关键**:Oracle 自己写了一行**"为什么死"**到 alert log(`Cause - 'Instance is being terminated due to fatal process death (..., SMON)'`)。Pod-kill 那一轮的 alert log 没有这种 cause 记录 — 因为整个进程组被 SIGKILL,没人有时间写 log。 + +### Observer 视角 + +``` +[W000 2026-05-15T04:09:34.912+00:00] Primary database cannot be reached. +[W000 2026-05-15T04:09:34.912+00:00] Fast-Start Failover threshold has not exceeded. Retry for the next 30 seconds +... (threshold 倒计时) ... +[W000 2026-05-15T04:10:04.996+00:00] Fast-Start Failover threshold has expired. +[W000 2026-05-15T04:10:04.996+00:00] Check if the standby is ready for failover. +[S005 2026-05-15T04:10:05.002+00:00] Fast-Start Failover started... +Initiating Fast-Start Failover to database "ORCLCDB_1"... +Performing failover NOW, please wait... +Failover succeeded, new primary is "ORCLCDB_1" +[W000 2026-05-15T04:10:37.582+00:00] Failover succeeded. Restart pinging. +[W000 2026-05-15T04:10:37.674+00:00] The standby ORCLCDB_0 needs to be reinstated +... (等 ORCLCDB_0 重启回来作为 standby) ... +[W000 2026-05-15T04:10:43.680+00:00] New primary is now ready to reinstate. +[W000 2026-05-15T04:10:43.680+00:00] Issuing REINSTATE command. +Initiating reinstatement for database "ORCLCDB_0"... +[W000 2026-05-15T04:11:19.747+00:00] The standby ORCLCDB_0 is ready to be a FSFO target +Reinstatement of database "ORCLCDB_0" succeeded +[W000 2026-05-15T04:11:42.791+00:00] Successfully reinstated database ORCLCDB_0. +``` + +### Broker 视角(关键 poll) + +``` +poll 1 (T+23s) ORCLCDB_0: Error ORA-1034 (ORACLE not available), status=ERROR + — 注意是 ORA-1034 不是 ORA-1033 / connection refused + — 因为 listener 还活着,只是 instance 不可用 +poll 2 (T+40s) ORCLCDB_0: 同上, oracle 容器 RESTARTS=1, kbagent 没动 +poll 3 (T+62s) ORCLCDB_1 = Primary ← 角色已切换 + ORCLCDB_0 = Physical standby (disabled), ORA-16661 needs reinstate + status=SUCCESS(status updated 59 seconds ago) +poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby +``` + +### 结果 + +- **FSFO 触发了** +- **角色发生了真正的切换**:故障前 primary 是 ORCLCDB_0,故障后 primary 是 ORCLCDB_1 +- **Auto-reinstate 自动把原 primary 拉回来当 standby**,全程无人工干预 +- 整个 cluster 从故障到完全恢复角色 + reinstate ≈ 150 秒 + +### 为什么 FSFO 触发了 + +跟 round 1 同一个 cluster、同一个 broker 配置、同一个 FSFO threshold,为什么这次能 fail over? + +1. **故障是进程级的**:oracle main process 被 watchdog 杀(PID 1 exit),但容器里的 listener、kbagent ctr、network stack 都保持活着 +2. **Standby 看到的不是 TCP RST,是 service-level 错**:broker poll 看到 ORA-1034(ORACLE not available),而不是 connection refused +3. **Standby 的 redo apply 没有被 abruptly 切断** — SMON kill 触发 PMON 主动终止 instance,PMON 有几毫秒清理 + dump 时间,最后一秒 redo 已经被 shipped/applied 或至少 standby 没收到"不完整"信号 +4. **Standby 没进 SUSPEND** — observer tick 看到 standby = healthy,threshold 倒计时正常累积 +5. T+37s(kill 后 30s + 几秒抖动)threshold expired → FSFO 触发 + +## 两轮关键差异并排 + +| 维度 | Round 1 — Pod-kill | Round 6 — SMON-kill | +|---|---|---| +| 故障注入 | `kubectl delete pod --force --grace-period=0` | `kubectl exec ... kill -9 ora_smon_*` | +| Pod 状态 | 整个 pod 被销毁重建(所有 4 个 ctr) | Pod 没动,仅 oracle ctr restart | +| Sidecar ctr 影响 | kbagent / exporter / config-manager 一起重启 | 完全不受影响 | +| Listener 状态 | 被杀掉,TCP 连接 RST | listener 进程不存在了(runOracle exit 时),但短暂窗口内 standby 看到的是 ORA-1034 | +| Alert log "why died" | **没有**(整个进程被 SIGKILL) | **有**(`Cause - 'fatal process death (..., SMON)'`) | +| Standby 状态 | **SUSPEND**(数据完整性护栏触发) | 保持 healthy(normal physical standby) | +| Observer 第一句反应 | `Standby is in the SUSPEND state. FSFO suspended. Reset FSFO timer.` | `FSFO threshold has not exceeded. Retry for the next 30 seconds` | +| FSFO threshold 累计 | 永远凑不齐(每次 reset) | 正常累计到 30s expired | +| **FSFO 触发?** | **否** | **是** | +| 角色切换? | 否(self-recovery 回原 role) | 是(ORCLCDB_0 → ORCLCDB_1) | +| Auto-reinstate? | n/a | 是(原 primary 自动回归当 standby) | +| Broker 完整恢复时间 | ~157s(无角色变) | ~150s(含角色变 + reinstate) | +| F37(kbagent ctr 无 symlink) | 触发(pod restart) | **不触发**(只 oracle ctr restart) | + +## 结论:故障层级不同 → failover 行为不同 → chaos 矩阵必须双轴覆盖 + +### 关键事实 + +**这不是 bug,也不是 feature** — 是 Oracle DG FSFO 在 MaxPerformance + ASYNC 模式下的**正确行为**。FSFO 的设计要求 standby 处于 valid + non-suspend 状态才允许 failover,这是数据丢失保护。Pod-kill 这种"全员瞬间断电"的故障模式天然让 standby 退到 SUSPEND;SMON-kill 这种"primary 内部死亡 + 网络栈短暂存活"让 standby 始终看着 healthy。 + +### 运维含义 + +| 生产场景 | 对应 chaos 轴 | FSFO 是否会救你 | +|---|---|---| +| 节点宕机、OS reboot、整 pod 被驱逐 | A(pod-kill 类) | 在 MaxPerformance + ASYNC 下,**多数情况下不会** — 需要等 pod 重启自愈,或人工 failover | +| OOM kill 一个 ora_* 后台进程(Oracle 内部断言、内存压力) | B(内部 crash 类) | **会**触发 FSFO | +| `shutdown abort` / 引擎内部死锁导致 instance 自杀 | B | **会**触发 FSFO | +| 容器 liveness probe 失败 kubelet kill 容器 | A 倾向(整 ctr 被 kill) | 同 A — 看 standby 是否被打到 SUSPEND | + +> **运维侧实操结论**:如果业务要求"primary 任何形式失联都自动 failover",MaxPerformance + ASYNC 不够。需要切到 MaxAvailability + SYNC + 合适的 NoDataLossTimeout。但代价是 primary 写可用性可能在 standby 不可达时被 hold。本 cluster 用 MaxPerformance + ASYNC 是显式选择了"保写可用性,接受单点数据丢失窗口"。 + +### Chaos 测试侧实操结论 + +**只测 pod-kill 是不够的**。如果 chaos 矩阵只跑 round 1 这种 K8s 层故障,你只验证了 ASYNC + SUSPEND-protection 路径下的 FSFO 行为 — 这条路径里 FSFO 通常不触发。你完全没验证 FSFO 自己的 threshold 累计 + failover 决策 + auto-reinstate 链路。 + +至少要再加一轮 round 6 这种引擎内部进程级 kill,才能: + +1. 验证 FSFO threshold 倒计时器是否工作(之前每次都被 SUSPEND reset 掉) +2. 验证 broker 真正 issue `Initiating Fast-Start Failover...` 的代码路径 +3. 验证 auto-reinstate(`FastStartFailoverAutoReinstate = TRUE`)是否正确把老 primary 拉回来 +4. 验证 FSFO 完整恢复时间 SLO(150s 量级 vs 仅 self-recovery 的 157s) + +## 相关 finding 联动 + +这次对照测试还顺带验证了另外几条: + +- **F37 路径只在 pod-restart 触发,不在 ctr-restart 触发**:round 1 触发 F37(kbagent ctr 没 symlink),round 6 不触发(kbagent ctr 没被重启,symlink 保留)。这是 F37 的边界条件证据。 +- **runOracle.sh PID-1 watchdog 工作正确**:round 6 中 `pgrep -f ora_pmon_${ORACLE_SID}` 30s tick 检测到 instance terminated,PID 1 exit,kubelet 立即 restart oracle ctr。从 SMON kill 到新 instance starting ≈ 41s — 比 K8s liveness probe failureThreshold 快得多。 +- **FSFO threshold 30s 设置合理**:观察到 observer 从"primary cannot be reached"到"threshold expired"刚好 30s,与配置一致;如果调到 10s 会有更快 failover 但 round 1 这种 transient outage 也会被错误触发。 + +## 案例文件清单 + +``` +/Users/wei/ApeCloud/evidence-skyworth-oracle-19c/ +├── chaos-primary-kill-2026-05-15/ +│ ├── FINDINGS.md +│ ├── during/00-kill-event.txt +│ ├── during/01-broker-poll.txt +│ ├── during/02-pod-stream.txt +│ └── post/00b-observer-full.txt +└── chaos-smon-kill-primary-2026-05-15/ + ├── FINDINGS.md + ├── during/00-kill-event.txt + ├── during/01-broker-poll.txt + ├── during/02-alert-log-extract.txt + ├── during/03-observer-log-snapshot.txt + └── during/03-observer-log.txt +``` + +## 与其他案例 / 方法论的关系 + +- 方法论母文档:[`../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) — 本案例是它的 Oracle 工程现场补充 +- 同 cluster 上 round 3/4/5 的其他 chaos 轮次:见 `evidence-skyworth-oracle-19c/chaos-{fsfo-target-kill,observer-kill,dual-standby-kill}-*` 各自 FINDINGS.md +- F34 / F35 / F36 / F37 多容器共享文件相关 finding:见 [`oracle-f35-sync-membership-grace-period-case.md`](oracle-f35-sync-membership-grace-period-case.md) 与 [`../../addon-multi-container-shared-file-symlink-write-guide.md`](../../addon-multi-container-shared-file-symlink-write-guide.md) From 067a712dd08273328ba58a786ab1a681d37b14c9 Mon Sep 17 00:00:00 2001 From: Ava Date: Fri, 15 May 2026 12:44:36 +0800 Subject: [PATCH 2/9] docs: refine engine-internal crash into B-instance vs B-broker classes Chaos round 9 (DMON SIGKILL on primary) discovered a 3rd failure class that the 2-axis taxonomy missed: - B-instance (e.g. SMON / LGWR): PMON terminates the instance, watchdog restarts ctr, FSFO fires, role change + reinstate. - B-broker (e.g. DMON / INSV / NSV*): engine internally spawns a new process, instance unaffected, no ctr restart, FSFO must NOT fire. The B-broker class exposes a distinct design risk: if a failover decision-maker conflates "broker config query failed" with "primary unreachable", it will mis-trigger failover. The new test matrix row (#8) covers this explicitly. Also extended the Oracle case appendix with rounds 7 (LGWR), 8 (observer+SMON race) and 9 (DMON) summaries + file map, and noted F38 (observer setup script has no timeout) as a related latent risk. --- ...pod-kill-vs-engine-internal-crash-guide.md | 96 ++++++++++++++----- ...d-kill-vs-smon-kill-fsfo-asymmetry-case.md | 46 +++++++-- 2 files changed, 108 insertions(+), 34 deletions(-) diff --git a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md index faff07c..870ba60 100644 --- a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md +++ b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md @@ -10,14 +10,15 @@ 很多 chaos 测试默认 "杀 pod" 就等于 "测试了节点故障"。其实远远不够。 -同一个 DB 实例的"崩溃"有两种很不一样的形态: +同一个 DB 实例的"崩溃"有三种很不一样的形态: 1. **K8s 层**:`kubectl delete pod --force --grace-period=0`、节点宕机、Pod evict。Pod 整个消失,K8s 重建。 -2. **引擎内部**:DB 关键后台进程崩溃(SMON / LGWR / WAL writer 等)、OOM 杀掉了一个进程、引擎内部断言 fail 自杀。Pod 还在,容器还在,但 DB 实例死了。 +2. **引擎内部 — instance critical**:DB 关键后台进程崩溃(Oracle SMON/LGWR、PostgreSQL postmaster 等),引擎自己判断"这进程死了我整个 instance 不能继续",触发 instance 终止。Pod 还在,容器还重启了,但 DB instance 死过一次。 +3. **引擎内部 — broker / 辅助守护进程**:仅与 HA 协议、复制协议、监控相关的进程崩溃(Oracle DMON/INSV/NSV*、MySQL semi-sync 插件线程等)。引擎**自己悄悄重启该进程**,instance 不停,容器不重启,外部完全看不见。 -这两种故障在生产里都会出现,但它们**走完全不同的恢复路径**,因此**也可能触发完全不同的 failover 行为**。一个测了不代表测了另一个。 +这三种故障在生产里都会出现,但它们**走完全不同的恢复路径**,因此**也可能触发完全不同的 failover 行为**。一个测了不代表测了另一个。 -本文给出一组明确的口径,让你在 chaos 矩阵里把"K8s 层杀"和"引擎内部 crash"分两个轴显式覆盖。 +本文给出一组明确的口径,让你在 chaos 矩阵里把这三类故障分别显式覆盖。 ## 适用场景 @@ -28,7 +29,7 @@ - 现场观察到生产里有 failover 现象,但本地 chaos 没复现 — 排查是不是测错了轴。 - 别的 team 跑通了某种 chaos 你跑不通(或反过来)— 检查是不是两人测的不是同一个轴。 -## 核心边界:两轴各自的恢复路径 +## 核心边界:三类各自的恢复路径 ### 轴 A — K8s 层 pod kill @@ -48,14 +49,14 @@ init container → main container 启动 → 引擎 bootstrap → 引擎主进 - Pod 重建可能耗时数十秒到几分钟(PVC mount、image cache、bootstrap 逻辑) - kbagent / sidecar 容器**也跟着重启** — 任何依赖 sidecar 启动期间临时做的事(如 symlink)都会重新做或丢 -### 轴 B — 引擎内部 crash +### 轴 B-instance — 引擎内部 critical 后台进程 crash(instance 终止) ``` kubectl exec -c -- kill -9 ↓ -引擎内部检测到关键进程死,触发 instance 自我终止 +引擎内部检测到关键进程死,判断 "instance 不能继续" ↓ -引擎清理 + 写 alert log +引擎自我终止 instance + 写 alert log ↓ 容器 PID 1 watchdog 检测引擎死,exit ↓ @@ -69,41 +70,77 @@ kubelet restart 容器(同一 pod 内) - 网络连接**可能短暂保持**(listener 进程独立存活,但服务无响应)— 远端 standby / observer 看到 ORA-1034 类的"service unavailable"而不是 connection refused - **只有 main 容器重启**,sidecar 容器(kbagent / exporter)保持原状 — sidecar 内的 ephemeral 文件保留 - 整体恢复通常**更快**(共享 PVC 不用重 mount、网络栈不重建) +- **FSFO / failover 通常会被触发** — standby 看到的是干净的"primary 死了但 standby 还健康" + +典型例子:Oracle SMON / LGWR、PostgreSQL postmaster、MySQL InnoDB main thread。判断标准:**引擎内核把这个进程视为 instance-critical**,进程一死整个 instance 就要终止。 + +### 轴 B-broker — 引擎内部 broker / 辅助守护进程 crash(instance 不动) + +``` +kubectl exec -c -- kill -9 + ↓ +引擎内部检测到该进程死 + ↓ +引擎 spawn 一个新的同名进程(pid 变了,instance 没动) + ↓ +关联辅助进程(如 Oracle DMON 的 INSV / NSV* / RSM0 / FSFP)逐步重起 + ↓ +broker / 协议层短暂 ERROR,几十秒内回到 SUCCESS +``` + +**特征**: +- **Instance 完全不停**,main 容器**不重启**,K8s 层完全感知不到 +- 网络与读写流量**不中断** +- broker / 协议层有一个"短暂 ERROR 窗口"(典型 10-30s),期间 failover 决策可能临时不可用 +- **FSFO 不会触发** — 因为 observer 对 primary instance 的连通性检查仍然正常返回 +- 恢复时间是三类里最短的(典型 20-30 秒,全部发生在引擎内部) + +典型例子:Oracle DMON / INSV / NSV* / RSM0 / FSFP / LREG,MySQL semi-sync ACK 线程、复制协议线程。判断标准:**进程死后引擎只是 spawn 一个新的同名进程**,instance 不动。 + +**重点**:B-broker 测试的不是"引擎能不能恢复 instance"——instance 根本没倒;而是**"协议层短暂中断期间,依赖该协议的外部组件(observer、监控、自动 failover 决策器)会不会误判"**。 ## 为什么 failover 行为会不同 Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联**: - 轴 A — TCP 直接断开:standby/observer 可能立刻判定"primary 不可达",但同时也判定"我已经 out-of-sync"(因为我们用 async transport 时 standby 没收到最后一秒的 redo)。某些保护模式下,standby 会进入 SUSPEND 状态以防止数据丢失 promotion。**FSFO 可能因 standby SUSPEND 而暂停**。 -- 轴 B — 引擎崩溃但 listener 还活着:standby/observer 看到 service-level 错误("instance not available")但 TCP 通路还在;standby 自己最后一次 redo apply 可能刚好接近 synced;observer 看到的是干净的"primary 死了,但 standby 还是 healthy"的状态。**FSFO 触发的几率更高**。 +- 轴 B-instance — 引擎崩溃但 listener 还活着:standby/observer 看到 service-level 错误("instance not available")但 TCP 通路还在;standby 自己最后一次 redo apply 可能刚好接近 synced;observer 看到的是干净的"primary 死了,但 standby 还是 healthy"的状态。**FSFO 触发的几率更高**。 +- 轴 B-broker — instance 完全没倒,只有 broker 协议层短暂不可用:observer 对 primary 的连通性检查仍然正常返回;只有 broker 配置查询会瞬间返回 ERROR。**FSFO 不该触发**——这是正确的;broker 协议短暂 ERROR 不能等同于 primary 失联。 + +**这不是 bug 也不是 feature — 是不同故障模式天然的不同信号** — 只是 chaos test 必须把这三类故障显式测,否则你只验证了一类信号下的 failover。 -**这不是 bug 也不是 feature — 是不同故障模式天然的不同信号** — 只是 chaos test 必须把这两个轴显式测,否则你只验证了一类信号下的 failover。 +特别要点:**"broker 协议 ERROR" ≠ "primary 不可达"**。如果你的 failover 决策器把 broker 协议查询失败也当作 primary 失联触发,那它在 B-broker 类故障下会**误触发 failover**——这是 chaos 矩阵能暴露的设计缺陷,必须显式测。 ## 测试矩阵建议 最小覆盖矩阵(不分引擎通用): -| # | 轴 | 故障注入 | 期望验证 | +| # | 类 | 故障注入 | 期望验证 | |---|---|---|---| | 1 | A | primary pod kill | engine self-recovery / FSFO 行为 / 写可用性 | | 2 | A | standby pod kill (非 failover 目标) | standby 自愈,primary 不受影响 | | 3 | A | FSFO target pod kill | target auto-shift / 失联期间 FSFO blind window | | 4 | A | 全部 standby 同时 kill | primary 写可用性(async/sync 模式差异) | | 5 | A | observer (or failover decision-maker) pod kill | observer 自动 reconnect、FSFO blind window | -| 6 | **B** | **primary 内部关键进程 kill** | **engine self-restart watchdog 工作 / FSFO 行为差异(与 #1 对比)** | -| 7 | B | standby 内部关键进程 kill | standby self-restart | -| 8 | A | rolling kill across pods | 序列失败下的稳定性 | +| 6 | **B-instance** | **primary 内部 instance-critical 进程 kill** | **engine self-restart watchdog 工作 / FSFO 触发(与 #1 对比)** | +| 7 | B-instance | standby 内部 instance-critical 进程 kill | standby self-restart,primary 不受影响 | +| 8 | **B-broker** | **primary broker / 协议层进程 kill** | **引擎内部自动重起 broker,instance 不动,FSFO 不该触发** | +| 9 | A+B-instance | observer kill + primary instance-critical 进程 kill | self-recovery vs observer-restart 谁赢的 race | +| 10 | A | rolling kill across pods | 序列失败下的稳定性 | -**核心契约**: 至少 #1 (轴 A) 和 #6 (轴 B) 都必须做,并显式比较它们的 FSFO 行为。仅做 #1 = chaos 覆盖不完整。 +**核心契约**: 至少 #1 (A) + #6 (B-instance) + #8 (B-broker) 三类都必须做,并显式比较它们的 FSFO 行为。仅做 #1 = chaos 覆盖不完整。仅做 #1 + #6 = 漏掉了"broker 短暂 ERROR ≠ primary 失联"的判定缺陷暴露面。 ## 引擎适配 checklist -不同引擎的"关键后台进程"不一样。设计 #6/#7 时先确认这些: +不同引擎的进程分类不一样。设计 #6/#7/#8 时先确认这些: -1. **哪些进程是 critical(杀了导致整个 instance 终止)**:例如 Oracle 的 PMON / SMON / LGWR、MySQL 的 ibwriter 内核线程、PostgreSQL 的 postmaster 主进程 -2. **引擎自己写不写 crash 原因到 alert/error log**:决定测试结束后能不能取得"为什么死"的证据 -3. **容器 PID 1 是谁**:是 init system?是 entrypoint script?entrypoint script 有没有写 watchdog 检测引擎死? -4. **如果 PID 1 不死、引擎死了,容器会不会被 K8s 回收**:取决于 liveness probe 是否能检测到、failureThreshold 多少。watchdog(PID 1 主动 exit)比 liveness probe 快得多 +1. **哪些进程是 instance-critical(杀了导致整个 instance 终止)**:例如 Oracle 的 PMON / SMON / LGWR、PostgreSQL 的 postmaster 主进程、MySQL 的 InnoDB main thread +2. **哪些进程是 broker / 辅助守护(杀了引擎自己 spawn 一个新的,instance 不动)**:例如 Oracle 的 DMON / INSV / NSV* / RSM0 / FSFP / LREG、MySQL semi-sync 插件线程。**这一类需要专门验证 — 别把它和 #1 混到一起测** +3. **引擎自己写不写 crash 原因到 alert/error log**:决定测试结束后能不能取得"为什么死"的证据 +4. **容器 PID 1 是谁**:是 init system?是 entrypoint script?entrypoint script 有没有写 watchdog 检测引擎死? +5. **如果 PID 1 不死、引擎死了,容器会不会被 K8s 回收**:取决于 liveness probe 是否能检测到、failureThreshold 多少。watchdog(PID 1 主动 exit)比 liveness probe 快得多 +6. **failover 决策器 / observer / 监控**对 broker 协议 ERROR 的判定:会不会把"broker 配置查询失败"误当成"primary 失联"?如果会,B-broker 类故障会触发不必要的 failover +7. **observer / failover 决策器自身的恢复路径**:如果它有 setup 脚本(比如 Oracle observer 的 setup_observer.sh),脚本里有没有在等某个数据库实例 OPEN?有没有 timeout 兜底?没有的话,instance 不可恢复时 observer 永远起不来 → FSFO 永久 blind ## 验收口径 @@ -117,11 +154,13 @@ Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联** ## 反模式 -1. **"杀 pod 跑通了,就过了"** — 只覆盖了轴 A,FSFO 在轴 B 下的行为没测。 -2. **"OOM kill 容器 = 内部 crash"** — 错。OOM kill 把整个容器杀掉,是轴 A 类型;要测轴 B 必须 SIGKILL 引擎进程而不打容器。 -3. **"标准故障注入工具(chaos-mesh / litmus)够用了"** — 这些工具默认主要做轴 A(pod kill / network partition / node drain)。轴 B 需要 `kubectl exec ... kill -9 `,没几个 chaos 框架原生支持。 -4. **"内部 crash 测试太危险,不在 prod 跑"** — chaos 本来就是在 staging / pre-prod 跑。轴 B 的内部 crash 在 prod 里**实际上每天发生** — 只是你不知道而已(DBA 看 alert log 才能看到)。 -5. **"只测一次轴 B 就够"** — 应该把不同 critical 进程都测一遍。SMON 和 LGWR 的 fail-out 路径可能不同。 +1. **"杀 pod 跑通了,就过了"** — 只覆盖了轴 A,FSFO 在 B-instance / B-broker 下的行为没测。 +2. **"OOM kill 容器 = 内部 crash"** — 错。OOM kill 把整个容器杀掉,是轴 A 类型;要测 B 类必须 SIGKILL 引擎进程而不打容器。 +3. **"标准故障注入工具(chaos-mesh / litmus)够用了"** — 这些工具默认主要做轴 A(pod kill / network partition / node drain)。B 类需要 `kubectl exec ... kill -9 `,没几个 chaos 框架原生支持。 +4. **"内部 crash 测试太危险,不在 prod 跑"** — chaos 本来就是在 staging / pre-prod 跑。B 类内部 crash 在 prod 里**实际上每天发生** — 只是你不知道而已(DBA 看 alert log 才能看到)。 +5. **"只测一次 B-instance 就够"** — 应该把不同 instance-critical 进程都测一遍。SMON 和 LGWR 的 fail-out 路径可能不同。 +6. **"B-instance 测过了,B-broker 不用单独测"** — 错。两类的恢复路径根本不同:B-instance 走 ctr-restart 路径,B-broker 走引擎内部 spawn 路径。前者验证 watchdog + FSFO,后者验证"协议层短暂 ERROR 不会误触发 failover"。 +7. **"B-broker instance 没倒,所以没意义"** — 错。B-broker 暴露的是 **failover 决策器的误判设计缺陷**——你能不能区分"primary 失联"和"broker 配置查询失败"。这个区分错了,B-broker 故障会被误升级为不必要的 failover。 ## 与其他 chaos / 测试方法论的关系 @@ -133,4 +172,9 @@ Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联** 引擎相关的实测命令、日志样本、recovery 时间数据放在独立案例材料里: -- [`cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c on KubeBlocks o19p15v9 cluster: 6 轮 chaos 矩阵实测,pod-kill (round 1) 与 SMON-kill (round 6) 的 FSFO 行为对比 + 完整 alert log + observer log + broker poll 轨迹 +- [`cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c on KubeBlocks o19p15v9 cluster: 9 轮 chaos 矩阵实测,覆盖三类故障的对比 + 完整 alert log + observer log + broker poll 轨迹。其中: + - Round 1 (A 类 primary pod-kill) — FSFO 因 standby SUSPEND 抑制 + - Round 6 (B-instance SMON kill) — FSFO 触发,role 切换,自动 reinstate + - Round 7 (B-instance LGWR kill) — 验证 B-instance 行为不止 SMON + - Round 8 (A+B-instance observer+SMON 并发) — primary self-recovery 赢 race + - Round 9 (B-broker DMON kill) — 引擎内部自愈,instance 不动,FSFO 不触发 diff --git a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md index 917ad98..c86e183 100644 --- a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md +++ b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md @@ -209,21 +209,51 @@ poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby ``` /Users/wei/ApeCloud/evidence-skyworth-oracle-19c/ -├── chaos-primary-kill-2026-05-15/ +├── chaos-primary-kill-2026-05-15/ # round 1 (A) │ ├── FINDINGS.md │ ├── during/00-kill-event.txt │ ├── during/01-broker-poll.txt │ ├── during/02-pod-stream.txt │ └── post/00b-observer-full.txt -└── chaos-smon-kill-primary-2026-05-15/ - ├── FINDINGS.md - ├── during/00-kill-event.txt - ├── during/01-broker-poll.txt - ├── during/02-alert-log-extract.txt - ├── during/03-observer-log-snapshot.txt - └── during/03-observer-log.txt +├── chaos-smon-kill-primary-2026-05-15/ # round 6 (B-instance, SMON) +│ ├── FINDINGS.md +│ ├── during/00-kill-event.txt +│ ├── during/01-broker-poll.txt +│ ├── during/02-alert-log-extract.txt +│ ├── during/03-observer-log-snapshot.txt +│ └── during/03-observer-log.txt +├── chaos-lgwr-kill-primary-2026-05-15/ # round 7 (B-instance, LGWR) +│ └── FINDINGS.md +├── chaos-observer-and-primary-smon-2026-05-15/ # round 8 (A+B-instance concurrent) +│ └── FINDINGS.md +├── chaos-dmon-kill-primary-2026-05-15/ # round 9 (B-broker, DMON) +│ └── FINDINGS.md +└── F38-observer-setup-no-timeout/ # latent risk discovered during round 8 + └── FINDING.md ``` +## Round 7 / 8 / 9 关键发现摘要 + +### Round 7 — B-instance generality (LGWR kill) +LGWR 死亡的恢复路径与 round 6 SMON kill **结构上一致**:T+5s PMON 检测到、T+8s instance terminated、T+42s 新 instance starting、T+38s FSFO threshold expired、T+82s 完成 failover、T+139s broker SUCCESS。**证明 B-instance 行为不是 SMON-specific**。 +新观察:observer 在 standby reinstate 期间会因为 ORA-16657 重试 1-3 次,最终 settle。这是 normal eventual-consistency,不是 bug。 + +### Round 8 — A+B-instance 并发 race +observer pod kill 与 primary SMON kill 间隔 118ms 执行。primary self-recovery (T+19s 新 instance) 远快于 observer 重启 (T+56s observer ready)。observer 起来时 primary 已经活了 37s,**FSFO 没触发,没有 role change**。 +副产品 F38 — `setup_observer.sh` 用无 timeout 的 `while true` 等所有 instance OPEN;hardcode `${ORACLE_SID}_0` 作 primary 入口。如果 primary 永久不可恢复,observer 永远起不来,FSFO 永久 blind。已记录到 `evidence-skyworth-oracle-19c/F38-observer-setup-no-timeout/FINDING.md`。 + +### Round 9 — B-broker (DMON kill) +DMON SIGKILL 后 6 秒,Oracle 内部 `Completed: Data Guard Broker cleanup` → `Restarting Data Guard Broker (DMON)` → 新 DMON pid=2463 起来;T+9-19s 区间 INSV / NSV2 / NSV3 / RSM0 / FSFP 逐步起来;T+27s broker SUCCESS;T+31s redo transport destinations 重新 enable。**PMON pid 不变,oracle ctr 不重启,FSFO 不触发**。 +这是迄今为止 chaos 矩阵中破坏力最小的失败类型 — 全部 27 秒发生在 Oracle 内部,K8s 层完全感知不到。 + +| round | 类 | FSFO 触发 | role change | ctr restart | broker SUCCESS 时间 | +|---|---|---|---|---|---| +| 1 | A | NO (SUSPEND) | NO | 4 ctrs | ~157s | +| 6 | B-instance (SMON) | YES | 0→1 | oracle ctr only | ~150s | +| 7 | B-instance (LGWR) | YES | 1→0 | oracle ctr only | ~139s | +| 8 | A+B-instance (observer+SMON) | NO (race) | NO | observer + oracle ctr | ~80s | +| 9 | B-broker (DMON) | NO | NO | none | ~27s | + ## 与其他案例 / 方法论的关系 - 方法论母文档:[`../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) — 本案例是它的 Oracle 工程现场补充 From 40e1095a4b23284d24edbd628686912aaca9b38e Mon Sep 17 00:00:00 2001 From: Ava Date: Fri, 15 May 2026 12:50:18 +0800 Subject: [PATCH 3/9] docs: add round 10 (INSV kill) as B-broker generality test Round 10 killed INSV (broker support daemon) on the primary, paired with round 9's DMON (broker coordinator) kill, to verify the B-broker class generalizes beyond the master daemon. Result: INSV kill recovers in ~4 seconds with two alert log lines. No broker cleanup, no support-process cascade, no ERROR window. This contrasts with DMON kill's ~27s full broker re-init. Both share the defining property of the B-broker class: instance is not terminated and FSFO must not fire. So the 3-class taxonomy (A / B-instance / B-broker) stands. A finer master-vs-worker sub-classification is noted but deferred until operationally needed. --- ...aos-pod-kill-vs-engine-internal-crash-guide.md | 3 ++- ...s-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md index 870ba60..2c2fbc6 100644 --- a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md +++ b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md @@ -172,9 +172,10 @@ Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联** 引擎相关的实测命令、日志样本、recovery 时间数据放在独立案例材料里: -- [`cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c on KubeBlocks o19p15v9 cluster: 9 轮 chaos 矩阵实测,覆盖三类故障的对比 + 完整 alert log + observer log + broker poll 轨迹。其中: +- [`cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c on KubeBlocks o19p15v9 cluster: 10 轮 chaos 矩阵实测,覆盖三类故障的对比 + 完整 alert log + observer log + broker poll 轨迹。其中: - Round 1 (A 类 primary pod-kill) — FSFO 因 standby SUSPEND 抑制 - Round 6 (B-instance SMON kill) — FSFO 触发,role 切换,自动 reinstate - Round 7 (B-instance LGWR kill) — 验证 B-instance 行为不止 SMON - Round 8 (A+B-instance observer+SMON 并发) — primary self-recovery 赢 race - Round 9 (B-broker DMON kill) — 引擎内部自愈,instance 不动,FSFO 不触发 + - Round 10 (B-broker INSV kill) — 验证 B-broker 不限于 DMON,4 秒恢复,alert log 仅 2 行 diff --git a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md index c86e183..f507985 100644 --- a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md +++ b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md @@ -226,7 +226,9 @@ poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby │ └── FINDINGS.md ├── chaos-observer-and-primary-smon-2026-05-15/ # round 8 (A+B-instance concurrent) │ └── FINDINGS.md -├── chaos-dmon-kill-primary-2026-05-15/ # round 9 (B-broker, DMON) +├── chaos-dmon-kill-primary-2026-05-15/ # round 9 (B-broker master, DMON) +│ └── FINDINGS.md +├── chaos-insv-kill-primary-2026-05-15/ # round 10 (B-broker worker, INSV) │ └── FINDINGS.md └── F38-observer-setup-no-timeout/ # latent risk discovered during round 8 └── FINDING.md @@ -242,9 +244,13 @@ LGWR 死亡的恢复路径与 round 6 SMON kill **结构上一致**:T+5s PMON observer pod kill 与 primary SMON kill 间隔 118ms 执行。primary self-recovery (T+19s 新 instance) 远快于 observer 重启 (T+56s observer ready)。observer 起来时 primary 已经活了 37s,**FSFO 没触发,没有 role change**。 副产品 F38 — `setup_observer.sh` 用无 timeout 的 `while true` 等所有 instance OPEN;hardcode `${ORACLE_SID}_0` 作 primary 入口。如果 primary 永久不可恢复,observer 永远起不来,FSFO 永久 blind。已记录到 `evidence-skyworth-oracle-19c/F38-observer-setup-no-timeout/FINDING.md`。 -### Round 9 — B-broker (DMON kill) +### Round 9 — B-broker master (DMON kill) DMON SIGKILL 后 6 秒,Oracle 内部 `Completed: Data Guard Broker cleanup` → `Restarting Data Guard Broker (DMON)` → 新 DMON pid=2463 起来;T+9-19s 区间 INSV / NSV2 / NSV3 / RSM0 / FSFP 逐步起来;T+27s broker SUCCESS;T+31s redo transport destinations 重新 enable。**PMON pid 不变,oracle ctr 不重启,FSFO 不触发**。 -这是迄今为止 chaos 矩阵中破坏力最小的失败类型 — 全部 27 秒发生在 Oracle 内部,K8s 层完全感知不到。 + +### Round 10 — B-broker worker (INSV kill) +INSV SIGKILL 后 4 秒内,alert log 仅两行:`Starting background process INSV` → `INSV started with pid=107, OS id=5109`。没有 broker cleanup、没有支撑进程级联、没有 broker ERROR 窗口。比 DMON kill 还要轻量得多。**说明 B-broker 类下还有一层 master vs worker 的区分**:DMON 是 broker 协调器,杀了引发全 broker re-init;INSV / NSV* / RSM0 / FSFP / LREG 是 broker 支撑进程,杀了仅触发 generic 重新 spawn。 + +但两者共同点是:**instance 不动 / FSFO 不该触发**。这就是 B-broker 类的定义性质。如果将来某次测试发现 master vs worker 在 operational 层面有显著差别,再考虑做更细的二级分类。 | round | 类 | FSFO 触发 | role change | ctr restart | broker SUCCESS 时间 | |---|---|---|---|---|---| @@ -252,7 +258,8 @@ DMON SIGKILL 后 6 秒,Oracle 内部 `Completed: Data Guard Broker cleanup` | 6 | B-instance (SMON) | YES | 0→1 | oracle ctr only | ~150s | | 7 | B-instance (LGWR) | YES | 1→0 | oracle ctr only | ~139s | | 8 | A+B-instance (observer+SMON) | NO (race) | NO | observer + oracle ctr | ~80s | -| 9 | B-broker (DMON) | NO | NO | none | ~27s | +| 9 | B-broker master (DMON) | NO | NO | none | ~27s | +| 10 | B-broker worker (INSV) | NO | NO | none | ~4s | ## 与其他案例 / 方法论的关系 From 3c722a0091e211448ecaff8eef8f38be51295989 Mon Sep 17 00:00:00 2001 From: Ava Date: Fri, 15 May 2026 12:57:25 +0800 Subject: [PATCH 4/9] docs: add round 11 (B-instance on active FSFO target) + target-shift dimension Round 11 killed SMON on the active FSFO target standby (oracle-1). Outcome: FSFO did NOT fire (primary stayed healthy), but observer auto-shifted the active target ORCLCDB_1 -> ORCLCDB_2 after the 30s threshold elapsed. Same target-shift behavior as round 3 (pod-kill on FSFO target), only via the B-instance recovery path, which finishes ~3x faster (84s vs 4m25s). Methodology guide now treats FSFO fire, role change, target shift, and broker config-status trajectory as four independent observable dimensions, so testers do not conflate "target shifted" with "failover fired". --- ...pod-kill-vs-engine-internal-crash-guide.md | 11 +++++++++ ...d-kill-vs-smon-kill-fsfo-asymmetry-case.md | 23 +++++++++++++------ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md index 2c2fbc6..b753437 100644 --- a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md +++ b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md @@ -130,6 +130,16 @@ Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联** **核心契约**: 至少 #1 (A) + #6 (B-instance) + #8 (B-broker) 三类都必须做,并显式比较它们的 FSFO 行为。仅做 #1 = chaos 覆盖不完整。仅做 #1 + #6 = 漏掉了"broker 短暂 ERROR ≠ primary 失联"的判定缺陷暴露面。 +### FSFO 决策的三个独立维度 + +测试结果除"是否 fire FSFO"外,还应该分别记录: + +1. **角色变更 (role change)** — primary 是否变了。只有 #1/#6 类故障可能触发。 +2. **FSFO target shift** — active 待命 target 是否被 observer 自动切到另一个 standby。"target 失联但 primary 健康"时会触发,与 FSFO fire 互斥。 +3. **broker config 状态轨迹** — SUCCESS → ERROR → WARNING → SUCCESS 各阶段时长。短暂 ERROR 不算缺陷,长时间 ERROR 才需要分析。 + +target shift 是 #3 (A on target) 和 #6 标记为 "在 active target standby 上做"时都可能出现的中间状态,但本身**不构成 failover**。把它和 FSFO fire 分开记录,避免误报为 failover。 + ## 引擎适配 checklist 不同引擎的进程分类不一样。设计 #6/#7/#8 时先确认这些: @@ -179,3 +189,4 @@ Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联** - Round 8 (A+B-instance observer+SMON 并发) — primary self-recovery 赢 race - Round 9 (B-broker DMON kill) — 引擎内部自愈,instance 不动,FSFO 不触发 - Round 10 (B-broker INSV kill) — 验证 B-broker 不限于 DMON,4 秒恢复,alert log 仅 2 行 + - Round 11 (B-instance on active FSFO target) — target shift 但 FSFO 不 fire;同 round 3 行为,但走 B 路径快 3 倍 diff --git a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md index f507985..326bd11 100644 --- a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md +++ b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md @@ -230,6 +230,8 @@ poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby │ └── FINDINGS.md ├── chaos-insv-kill-primary-2026-05-15/ # round 10 (B-broker worker, INSV) │ └── FINDINGS.md +├── chaos-smon-kill-standby-2026-05-15/ # round 11 (B-instance on active FSFO target) +│ └── FINDINGS.md └── F38-observer-setup-no-timeout/ # latent risk discovered during round 8 └── FINDING.md ``` @@ -252,14 +254,21 @@ INSV SIGKILL 后 4 秒内,alert log 仅两行:`Starting background process I 但两者共同点是:**instance 不动 / FSFO 不该触发**。这就是 B-broker 类的定义性质。如果将来某次测试发现 master vs worker 在 operational 层面有显著差别,再考虑做更细的二级分类。 -| round | 类 | FSFO 触发 | role change | ctr restart | broker SUCCESS 时间 | +### Round 11 — B-instance on active FSFO target (SMON kill on standby) +oracle-1 当时是 active FSFO target,杀其 SMON:T+10s broker ERROR;T+23s oracle ctr restart 1→2(watchdog 路径);T+43s **observer auto-shift active target 到 ORCLCDB_2**(同 round 3 pod-kill 行为,只是走 B 路径);T+84s broker SUCCESS。**primary ORCLCDB_0 全程不变,FSFO 不 fire** — 正确,"target 不可用" ≠ "primary 不可用"。 +对照 round 3 (A class 同位置 pod kill 同样 target shift):恢复时间 84s vs 4m25s。B-instance 比 A 在 standby 节点上快 3 倍 — 因为 ctr-restart 保留 PVC mount、kbagent ctr、exporter ctr 和 image cache。 +30s target-shift latency = FSFO threshold,observer 用同一个 threshold 评估 target 健康。 + +| round | 类 | FSFO 触发 | role change | target shift | broker SUCCESS 时间 | |---|---|---|---|---|---| -| 1 | A | NO (SUSPEND) | NO | 4 ctrs | ~157s | -| 6 | B-instance (SMON) | YES | 0→1 | oracle ctr only | ~150s | -| 7 | B-instance (LGWR) | YES | 1→0 | oracle ctr only | ~139s | -| 8 | A+B-instance (observer+SMON) | NO (race) | NO | observer + oracle ctr | ~80s | -| 9 | B-broker master (DMON) | NO | NO | none | ~27s | -| 10 | B-broker worker (INSV) | NO | NO | none | ~4s | +| 1 | A primary | NO (SUSPEND) | NO | n/a | ~157s | +| 3 | A FSFO target | NO | NO | YES (1→2) | ~4m25s | +| 6 | B-instance primary (SMON) | YES | 0→1 | n/a | ~150s | +| 7 | B-instance primary (LGWR) | YES | 1→0 | n/a | ~139s | +| 8 | A+B-instance (observer+SMON) | NO (race) | NO | n/a | ~80s | +| 9 | B-broker master (DMON) | NO | NO | n/a | ~27s | +| 10 | B-broker worker (INSV) | NO | NO | n/a | ~4s | +| 11 | B-instance FSFO target (SMON) | NO | NO | YES (1→2) | ~84s | ## 与其他案例 / 方法论的关系 From 4265e06a31f257b2d380ddfa4fe843d86f071746 Mon Sep 17 00:00:00 2001 From: Ava Date: Fri, 15 May 2026 13:03:10 +0800 Subject: [PATCH 5/9] docs: add round 12 (non-target standby SMON) + B-instance position matrix Round 12 completes the B-instance position-dependency matrix. Killing SMON on the non-target standby produces the minimum-impact B-instance outcome: broker ERROR, single ctr restart, ~110s to broker SUCCESS. No target shift, no role change, no FSFO. Primary writes uninterrupted throughout. Combined with rounds 6/7 (primary) and round 11 (active FSFO target), the same SMON kill produces three qualitatively different cluster-level outcomes depending on the target role. Methodology guide now documents this as the B-instance position dependency rule: each of the three positions must be tested separately. --- ...s-pod-kill-vs-engine-internal-crash-guide.md | 13 +++++++++++++ ...pod-kill-vs-smon-kill-fsfo-asymmetry-case.md | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md index b753437..815e0ad 100644 --- a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md +++ b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md @@ -140,6 +140,18 @@ Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联** target shift 是 #3 (A on target) 和 #6 标记为 "在 active target standby 上做"时都可能出现的中间状态,但本身**不构成 failover**。把它和 FSFO fire 分开记录,避免误报为 failover。 +### B-instance 的位置依赖性(必测三个位置) + +同一个 kill 动作在不同位置上产生**完全不同**的 cluster 级结果,因此 "B-instance 测过了" 不等于 "B-instance 全部行为测过了"。最少需要测三个位置: + +| 位置 | FSFO fire | role change | target shift | +|---|---|---|---| +| primary | YES | YES | n/a | +| 活动 FSFO target 上的 standby | NO | NO | YES(observer 切到另一个 standby) | +| 非 target standby | NO | NO | NO | + +这三个位置的恢复路径在引擎内部是一样的(PMON 检测 → instance 终止 → watchdog → ctr restart → bootstrap),但 observer / failover 决策器对每一种情况的响应完全不同。chaos 矩阵必须分别覆盖。 + ## 引擎适配 checklist 不同引擎的进程分类不一样。设计 #6/#7/#8 时先确认这些: @@ -190,3 +202,4 @@ target shift 是 #3 (A on target) 和 #6 标记为 "在 active target standby - Round 9 (B-broker DMON kill) — 引擎内部自愈,instance 不动,FSFO 不触发 - Round 10 (B-broker INSV kill) — 验证 B-broker 不限于 DMON,4 秒恢复,alert log 仅 2 行 - Round 11 (B-instance on active FSFO target) — target shift 但 FSFO 不 fire;同 round 3 行为,但走 B 路径快 3 倍 + - Round 12 (B-instance on non-target standby) — 无 target shift / 无 FSFO / 无 role change,恢复 ~110s,是 B-instance 位置依赖性中的最小影响基线 diff --git a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md index 326bd11..c4223dd 100644 --- a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md +++ b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md @@ -232,6 +232,8 @@ poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby │ └── FINDINGS.md ├── chaos-smon-kill-standby-2026-05-15/ # round 11 (B-instance on active FSFO target) │ └── FINDINGS.md +├── chaos-smon-kill-nontarget-standby-2026-05-15/ # round 12 (B-instance on non-target standby) +│ └── FINDINGS.md └── F38-observer-setup-no-timeout/ # latent risk discovered during round 8 └── FINDING.md ``` @@ -254,6 +256,20 @@ INSV SIGKILL 后 4 秒内,alert log 仅两行:`Starting background process I 但两者共同点是:**instance 不动 / FSFO 不该触发**。这就是 B-broker 类的定义性质。如果将来某次测试发现 master vs worker 在 operational 层面有显著差别,再考虑做更细的二级分类。 +### Round 12 — B-instance on non-target standby (minimum-impact baseline) +杀 oracle-1(这时是非 target standby,round 11 把 active target shift 到了 ORCLCDB_2)的 SMON:T+16s broker ERROR;T+49s oracle ctr restart 2→3;**target 全程不变 (ORCLCDB_2),无 role change,无 FSFO**;T+110s broker SUCCESS。observer 对非 target standby 失联完全不做 target re-evaluation —— 这是正确的,因为 FSFO 只关心 primary + active target。 + +### B-instance 位置依赖性矩阵(rounds 6/7/11/12 合成) +同一个 SMON SIGKILL 在三个不同位置上产生三种完全不同的 cluster 级结果: + +| 位置 | FSFO fire | role change | target shift | primary 写 | +|---|---|---|---|---| +| primary (rounds 6, 7) | YES | YES (P → standby) | n/a | 中断 ~150s | +| active target standby (round 11) | NO | NO | YES (T → 另一 standby) | 不中断 | +| non-target standby (round 12) | NO | NO | NO | 不中断 | + +这就是为什么"测了一次 B-instance"并不意味着"测了 B-instance 的全部行为" — 每个位置要单独测。 + ### Round 11 — B-instance on active FSFO target (SMON kill on standby) oracle-1 当时是 active FSFO target,杀其 SMON:T+10s broker ERROR;T+23s oracle ctr restart 1→2(watchdog 路径);T+43s **observer auto-shift active target 到 ORCLCDB_2**(同 round 3 pod-kill 行为,只是走 B 路径);T+84s broker SUCCESS。**primary ORCLCDB_0 全程不变,FSFO 不 fire** — 正确,"target 不可用" ≠ "primary 不可用"。 对照 round 3 (A class 同位置 pod kill 同样 target shift):恢复时间 84s vs 4m25s。B-instance 比 A 在 standby 节点上快 3 倍 — 因为 ctr-restart 保留 PVC mount、kbagent ctr、exporter ctr 和 image cache。 @@ -269,6 +285,7 @@ oracle-1 当时是 active FSFO target,杀其 SMON:T+10s broker ERROR;T+23s | 9 | B-broker master (DMON) | NO | NO | n/a | ~27s | | 10 | B-broker worker (INSV) | NO | NO | n/a | ~4s | | 11 | B-instance FSFO target (SMON) | NO | NO | YES (1→2) | ~84s | +| 12 | B-instance non-target standby (SMON) | NO | NO | NO | ~110s | ## 与其他案例 / 方法论的关系 From 60cc2873d8799b8ecca6e3e34984b1a8d4e97e99 Mon Sep 17 00:00:00 2001 From: Ava Date: Fri, 15 May 2026 13:26:48 +0800 Subject: [PATCH 6/9] docs: add round 13 burn-in + FSFO probabilism methodology MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round 13 = 3 consecutive SMON kills on primary, no manual reinstate between cycles. Outcomes split 1/3 self-recovery / 2/3 FSFO — direct observation of FSFO bimodal distribution. Cluster self-stabilized through 3 cycles, reinstate time stable (~120-150s, no degradation). Methodology guide gains: - "B-instance primary FSFO probabilism" section: race between self-recovery total time and FSFO threshold, with watchdog tick phase as the dominant tie-breaker (engine-neutral) - Engine adaptation checklist item 8: tune watchdog tick vs FSFO threshold relationship deliberately to pick one regime - Burn-in methodology section: >=3 cycles, decoupled poll point, topology rotation, bimodal ratio observation - Anti-pattern 8 (single-shot != verified) and 9 (do not poll from the member being killed — observed stale broker state for ~75s) Oracle case appendix gains: - Round 13 burn-in narrative + per-cycle table + bimodal observation - Methodology self-disclosure on the polling-point bug found in cycle 3 - File map and round summary table updated Co-Authored-By: Claude Opus 4.7 --- ...pod-kill-vs-engine-internal-crash-guide.md | 50 ++++++++++++++++++- ...d-kill-vs-smon-kill-fsfo-asymmetry-case.md | 36 +++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md index 815e0ad..2e061d4 100644 --- a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md +++ b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md @@ -152,6 +152,31 @@ target shift 是 #3 (A on target) 和 #6 标记为 "在 active target standby 这三个位置的恢复路径在引擎内部是一样的(PMON 检测 → instance 终止 → watchdog → ctr restart → bootstrap),但 observer / failover 决策器对每一种情况的响应完全不同。chaos 矩阵必须分别覆盖。 +### B-instance 在 primary 上 FSFO 触发的概率性 + +同一种 kill 在同一个 cluster 上重复跑,**FSFO 不一定每次都 fire**。是否 fire 取决于三个变量构成的赛跑: + +1. **failover decision-maker(observer / FSFO 控制器)的 threshold** — 多久判定 "primary 失联"。例如 30s。 +2. **engine self-recovery 总时间** — 从 instance terminated 到 instance OPEN 的总时长,包括 watchdog tick + ctr restart + bootstrap。 +3. **watchdog 检测 tick 的相位** — kill 发生在 watchdog tick 周期的哪个位置;最坏情况要等接近一整个 tick interval 才被发现。 + +只要 self-recovery 总时间 < threshold,FSFO 就不 fire;反过来就 fire。同一 cluster 不同次跑的差异主要来自: + +- **镜像 cache 状态**:cold image 时 ctr restart 拉镜像可能多 20-40s +- **watchdog tick 相位**:kill 时刚错过 tick → 需要等一整个 tick interval 才被检测 +- **存储 mount / PVC 重新挂载耗时**:偶发抖动 10s 级别 + +实测在 30s threshold + 30s watchdog tick 配置下,同一 SMON kill 重复 N 次会出现 bimodal 分布:约 1/3 次走 self-recovery(FSFO 不 fire),约 2/3 次走 FSFO 路径。 + +这不是"测试不稳定",而是设计层面的真实属性。结论: + +- **不要把 "kill primary 必然导致 failover" 写进运维 SOP**——这条契约不可靠 +- **如果要 "always failover" 行为**:让 self-recovery 总时间 > threshold(拉长 watchdog tick 或缩短 threshold) +- **如果要 "prefer self-recovery if fast"**:让 self-recovery 总时间 < threshold(默认 30s/30s 倾向这一侧,但是不确定) +- **burn-in 必须显式覆盖 ≥3 次 same-class kill**,否则你只看到了 bimodal 分布的一侧,把局部观察当作全局契约 + +写法上:单次 round 的 PASS/FAIL 只代表"恢复到健康状态",对 "FSFO fire / not fire" 这两个 outcome 都要独立记录,不要混入 PASS 判定。**FSFO fire 与 cluster 健康恢复是两件事**。 + ## 引擎适配 checklist 不同引擎的进程分类不一样。设计 #6/#7/#8 时先确认这些: @@ -163,6 +188,26 @@ target shift 是 #3 (A on target) 和 #6 标记为 "在 active target standby 5. **如果 PID 1 不死、引擎死了,容器会不会被 K8s 回收**:取决于 liveness probe 是否能检测到、failureThreshold 多少。watchdog(PID 1 主动 exit)比 liveness probe 快得多 6. **failover 决策器 / observer / 监控**对 broker 协议 ERROR 的判定:会不会把"broker 配置查询失败"误当成"primary 失联"?如果会,B-broker 类故障会触发不必要的 failover 7. **observer / failover 决策器自身的恢复路径**:如果它有 setup 脚本(比如 Oracle observer 的 setup_observer.sh),脚本里有没有在等某个数据库实例 OPEN?有没有 timeout 兜底?没有的话,instance 不可恢复时 observer 永远起不来 → FSFO 永久 blind +8. **watchdog tick interval 与 FSFO threshold 之间的比例**:决定了 B-instance primary kill 是倾向 self-recovery 还是倾向 FSFO。两者接近时会出现概率性 fire;burn-in 时要显式覆盖这一现象。生产配置应该选定一侧的语义并把两者拉开 + +## Burn-in(重复 same-class kill)方法学 + +单次 chaos round 的 PASS 不等于 "这一类故障已经验证"。对于结果带概率分布的故障类(典型是 B-instance primary kill),burn-in 才能暴露完整行为: + +1. **连续做 ≥3 次相同 kind 的 kill**,每次中间只等到 broker 报告 SUCCESS + 拓扑稳定,不做任何手工清理/reinstate +2. **每次 cycle 记录三个独立维度**:FSFO fire (Y/N) / role change / target shift —— 用同一格式 +3. **轮询点必须避开本次 kill 的目标**:从其他成员(或专门留一台不杀的成员)上跑 broker / cluster client。**这一条 burn-in 比单 round 更容易踩坑**——在 multi-cycle 里 "目标" 是变化的 +4. **拓扑会随 FSFO 旋转**:burn-in 末态可能和起点不同(primary、active target 都可能轮换到不同位置);这是预期,验收的是"自愈不破"而不是"拓扑还原" +5. **观察 bimodal 比例**:如果 N=3 次出现 1Y/2N 或 2Y/1N,说明此 cluster 在当前配置下处于 self-recovery vs FSFO 的临界线;这是有用信号 + +burn-in 还能顺带验证: + +- reinstate 路径在压力下是否退化(多次 FSFO 后 reinstate 时间是否飘移) +- 容器 restart count 是否正确累加(不重置、不丢失) +- F37 类 pod-restart-bound 资产是否 burn-in 期间被破坏(burn-in 通常只 ctr restart,不 pod restart,所以这一类 finding 不该触发) +- 任何累积型资源泄漏(FD、tmpfile、PV 残留 lock) + +burn-in cycle 之间必要的小停顿(≥30s)用来让 reinstate 完成;不要在 reinstate 中途立即起下一 cycle,否则你测的是 "broker 进入分裂态怎么办" 而不是 same-class 的概率分布。 ## 验收口径 @@ -183,6 +228,8 @@ target shift 是 #3 (A on target) 和 #6 标记为 "在 active target standby 5. **"只测一次 B-instance 就够"** — 应该把不同 instance-critical 进程都测一遍。SMON 和 LGWR 的 fail-out 路径可能不同。 6. **"B-instance 测过了,B-broker 不用单独测"** — 错。两类的恢复路径根本不同:B-instance 走 ctr-restart 路径,B-broker 走引擎内部 spawn 路径。前者验证 watchdog + FSFO,后者验证"协议层短暂 ERROR 不会误触发 failover"。 7. **"B-broker instance 没倒,所以没意义"** — 错。B-broker 暴露的是 **failover 决策器的误判设计缺陷**——你能不能区分"primary 失联"和"broker 配置查询失败"。这个区分错了,B-broker 故障会被误升级为不必要的 failover。 +8. **"single-shot chaos round 通过 = FSFO 行为已验证"** — 错。FSFO 在 B-instance primary kill 下是概率性的(bimodal 分布)。单次 PASS 只代表你看到了 bimodal 的一侧,必须 ≥3 次 same-class kill 才能观察到分布的全貌。burn-in 是必要的,不是 nice-to-have。 +9. **"burn-in 期间从被杀的成员上做 broker 状态轮询"** — 错。被杀的成员上跑的 dgmgrl / broker client 在 ctr restart 期间会返回 stale cached state(甚至连续 60-90s 仍报 SUCCESS),导致测试方法学上漏检 ERROR 窗口。**永远从非本轮被杀的成员上轮询 broker 状态**。多 cycle burn-in 还要在每个 cycle 里重新选轮询点。 ## 与其他 chaos / 测试方法论的关系 @@ -194,7 +241,7 @@ target shift 是 #3 (A on target) 和 #6 标记为 "在 active target standby 引擎相关的实测命令、日志样本、recovery 时间数据放在独立案例材料里: -- [`cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c on KubeBlocks o19p15v9 cluster: 10 轮 chaos 矩阵实测,覆盖三类故障的对比 + 完整 alert log + observer log + broker poll 轨迹。其中: +- [`cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c on KubeBlocks o19p15v9 cluster: 13 轮 chaos 矩阵实测(其中 round 13 包含 3 cycle burn-in),覆盖三类故障的对比 + 完整 alert log + observer log + broker poll 轨迹 + burn-in 概率分布。其中: - Round 1 (A 类 primary pod-kill) — FSFO 因 standby SUSPEND 抑制 - Round 6 (B-instance SMON kill) — FSFO 触发,role 切换,自动 reinstate - Round 7 (B-instance LGWR kill) — 验证 B-instance 行为不止 SMON @@ -203,3 +250,4 @@ target shift 是 #3 (A on target) 和 #6 标记为 "在 active target standby - Round 10 (B-broker INSV kill) — 验证 B-broker 不限于 DMON,4 秒恢复,alert log 仅 2 行 - Round 11 (B-instance on active FSFO target) — target shift 但 FSFO 不 fire;同 round 3 行为,但走 B 路径快 3 倍 - Round 12 (B-instance on non-target standby) — 无 target shift / 无 FSFO / 无 role change,恢复 ~110s,是 B-instance 位置依赖性中的最小影响基线 + - Round 13 (B-instance primary SMON 3-cycle burn-in) — bimodal FSFO 分布直接观察:1/3 cycle self-recovery 赢 / 2/3 cycle FSFO fire;reinstate 在压力下无 degradation;burn-in 轮询点陷阱(从被杀成员上轮询 → stale broker state)暴露 diff --git a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md index c4223dd..5effaef 100644 --- a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md +++ b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md @@ -234,6 +234,11 @@ poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby │ └── FINDINGS.md ├── chaos-smon-kill-nontarget-standby-2026-05-15/ # round 12 (B-instance on non-target standby) │ └── FINDINGS.md +├── chaos-burnin-smon-primary-2026-05-15/ # round 13 burn-in (3 consecutive SMON kills on primary) +│ ├── SUMMARY.md +│ ├── cycle1/FINDINGS.md +│ ├── cycle2/00-summary.txt + 02-poll.txt +│ └── cycle3/00-summary.txt + 02-poll.txt └── F38-observer-setup-no-timeout/ # latent risk discovered during round 8 └── FINDING.md ``` @@ -286,6 +291,37 @@ oracle-1 当时是 active FSFO target,杀其 SMON:T+10s broker ERROR;T+23s | 10 | B-broker worker (INSV) | NO | NO | n/a | ~4s | | 11 | B-instance FSFO target (SMON) | NO | NO | YES (1→2) | ~84s | | 12 | B-instance non-target standby (SMON) | NO | NO | NO | ~110s | +| 13-c1 | B-instance primary SMON burn-in 第 1 次 | **NO (self-recovery 赢)** | NO | n/a | ~75s | +| 13-c2 | B-instance primary SMON burn-in 第 2 次 | YES | 0→2 | n/a | ~150s | +| 13-c3 | B-instance primary SMON burn-in 第 3 次 | YES | 2→0 | YES (0→1) | ~121s | + +### Round 13 — Burn-in 3 cycles,FSFO 概率性确诊 + +`chaos-burnin-smon-primary-2026-05-15/SUMMARY.md` + +连续 3 次同样的 SMON SIGKILL on primary,cycle 间不做任何手工 reinstate: + +| cycle | 时刻 (UTC) | pre-primary | post-primary | post-target | FSFO 触发 | +|---|---|---|---|---|---| +| 1 | 05:04:15Z | ORCLCDB_0 | ORCLCDB_0 (不变) | ORCLCDB_2 (不变) | NO | +| 2 | 05:11:39Z | ORCLCDB_0 | ORCLCDB_2 | ORCLCDB_0 | YES | +| 3 | 05:14:34Z | ORCLCDB_2 | ORCLCDB_0 | ORCLCDB_1 | YES | + +末态:primary ORCLCDB_0、active target ORCLCDB_1、ORCLCDB_2 为非 target standby —— 与 burn-in 前的拓扑结构上一致(只是 active target 从 ORCLCDB_2 旋转到了 ORCLCDB_1)。3 次 cycle 全部自愈,无人工干预。 + +**核心发现**: +- 3 次同样的 kill,1 次没 fire(self-recovery 赢),2 次 fire(FSFO 路径)—— 这是 bimodal 分布的直接观察 +- cycle 1 是**第 3 次** 观察到 self-recovery 赢 race(前两次是 round 8 observer+SMON 并发 + 隐式 ctr restart 暖镜像)。镜像 cache 暖到这种程度后 self-recovery 总时长 ~15-20s,明显低于 30s threshold +- cycle 1 → 2 → 3 之间 ctr restart 让镜像 cache 持续暖;理论上 self-recovery 应该越来越快才对,但 cycle 2 和 3 都 fire 了,说明 **watchdog tick 相位**是更主要的变量,比镜像 cache 状态更敏感 +- reinstate 时间在 3 cycle burn-in 下保持稳定(cycle 2 ~150s,cycle 3 ~121s,对照单次 round 6 ~150s / round 7 ~139s)—— 自愈机器在 stress 下无 degradation +- F37 / F34 在 burn-in 中没 regression:F34 symlink 仍在,F37 没被触发(pod 没重启) + +**Methodology bug 自我披露**: cycle 3 中,broker 轮询脚本继续从 `o19p15v9-oracle-2` 的 oracle ctr 上跑 dgmgrl,而本 cycle 杀的是 ORCLCDB_2(也在 oracle-2 pod 上)—— 被杀成员上的 dgmgrl 返回 stale cached state 长达 ~75s。这就是 burn-in 必须避开"从被杀成员轮询"的反例。已写入方法论 doc 的反模式 #9 和 burn-in 章节。 + +**生产侧操作建议**: +- 不要把"SMON kill 必然触发 failover"写进 SOP,FSFO 在当前 30s/30s 配置下是 bimodal 的 +- 想要 "always failover":把 watchdog tick 拉长到 ≥60s(让 self-recovery 总是输 race),或者把 FSFO threshold 缩短到 ≤15s +- 想要 "prefer self-recovery if fast":保持当前配置,但要接受 1/3 概率走 self-recovery、2/3 概率走 FSFO 的 bimodal 行为 ## 与其他案例 / 方法论的关系 From 4f6dd16d81e105429c1d3bef3c0e62eff7a1239c Mon Sep 17 00:00:00 2001 From: Ava Date: Fri, 15 May 2026 13:40:32 +0800 Subject: [PATCH 7/9] docs: add round 14 (B-listener silent failure) + F39 watchdog gap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round 14 introduces B-listener as a fourth chaos sub-class beyond A / B-instance / B-broker. Killing the TNS listener leaves engine and broker daemon intact, so all existing TCP sessions (broker peer-to-peer, redo transport, observer-to-instance) keep working and the broker reports SUCCESS indefinitely — even six minutes after the listener is gone. The alert log writes nothing. Only new connections fail with ORA-12541. This is a true silent failure. For the Oracle Addon specifically, runOracle.sh's watchdog only pgreps ora_pmon; tnslsnr is never checked, so a listener crash never triggers ctr restart and never produces operator-visible signal. Filed as F39, to be fixed in a separate engine PR rather than mixed with this docs PR. Methodology guide gains: - A fourth axis (B-listener) with explanation of why existing TCP sessions mask the failure - Matrix row #11 and a stronger contract: must test #1 + #6 + #8 + #11 - Engine adaptation checklist item 9: listener / port-listener watchdog coverage - Anti-pattern 10: "broker SUCCESS = cluster healthy" is wrong; broker SUCCESS only reflects already-established TCP sessions Oracle case appendix gains: - Round 14 narrative, F39 fix sketch, file map, summary table row Co-Authored-By: Claude Opus 4.7 --- ...pod-kill-vs-engine-internal-crash-guide.md | 7 +++- ...d-kill-vs-smon-kill-fsfo-asymmetry-case.md | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md index 2e061d4..053201f 100644 --- a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md +++ b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md @@ -106,6 +106,7 @@ Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联** - 轴 A — TCP 直接断开:standby/observer 可能立刻判定"primary 不可达",但同时也判定"我已经 out-of-sync"(因为我们用 async transport 时 standby 没收到最后一秒的 redo)。某些保护模式下,standby 会进入 SUSPEND 状态以防止数据丢失 promotion。**FSFO 可能因 standby SUSPEND 而暂停**。 - 轴 B-instance — 引擎崩溃但 listener 还活着:standby/observer 看到 service-level 错误("instance not available")但 TCP 通路还在;standby 自己最后一次 redo apply 可能刚好接近 synced;observer 看到的是干净的"primary 死了,但 standby 还是 healthy"的状态。**FSFO 触发的几率更高**。 - 轴 B-broker — instance 完全没倒,只有 broker 协议层短暂不可用:observer 对 primary 的连通性检查仍然正常返回;只有 broker 配置查询会瞬间返回 ERROR。**FSFO 不该触发**——这是正确的;broker 协议短暂 ERROR 不能等同于 primary 失联。 +- 轴 B-listener — instance + broker daemon 都正常,只有**对外服务监听器 (listener / TNS)** 死了。这是真正的"静默故障"轴:所有现存 TCP 长连接(broker peer-to-peer / redo transport / observer ↔ instance)继续工作,但**新连接**被拒。broker 在 observation window 里**完全报告 SUCCESS**;alert log 不写一行;watchdog(如果只 pgrep 进程监控引擎主进程)不触发。这一类故障比 B-broker 还隐蔽——B-broker 至少在重启 broker 协议时有一窗口的 ERROR;B-listener 是永久 SUCCESS-but-broken。 **这不是 bug 也不是 feature — 是不同故障模式天然的不同信号** — 只是 chaos test 必须把这三类故障显式测,否则你只验证了一类信号下的 failover。 @@ -127,8 +128,9 @@ Failover 决策的依据是 **standby / observer 怎么"看到"primary 失联** | 8 | **B-broker** | **primary broker / 协议层进程 kill** | **引擎内部自动重起 broker,instance 不动,FSFO 不该触发** | | 9 | A+B-instance | observer kill + primary instance-critical 进程 kill | self-recovery vs observer-restart 谁赢的 race | | 10 | A | rolling kill across pods | 序列失败下的稳定性 | +| 11 | **B-listener** | **任意位置 listener (tnslsnr / port-listener) 杀死** | **silent failure 检测:watchdog 是否覆盖、broker 是否察觉、新连接是否失败、有没有自愈** | -**核心契约**: 至少 #1 (A) + #6 (B-instance) + #8 (B-broker) 三类都必须做,并显式比较它们的 FSFO 行为。仅做 #1 = chaos 覆盖不完整。仅做 #1 + #6 = 漏掉了"broker 短暂 ERROR ≠ primary 失联"的判定缺陷暴露面。 +**核心契约**: 至少 #1 (A) + #6 (B-instance) + #8 (B-broker) + #11 (B-listener) 四类都必须做,并显式比较它们的 FSFO 行为以及 watchdog 覆盖。仅做 #1 = chaos 覆盖不完整。仅做 #1 + #6 = 漏掉了"broker 短暂 ERROR ≠ primary 失联"的判定缺陷暴露面。漏 #11 = 永远不知道你的 watchdog 有没有 listener 盲区。 ### FSFO 决策的三个独立维度 @@ -189,6 +191,7 @@ target shift 是 #3 (A on target) 和 #6 标记为 "在 active target standby 6. **failover 决策器 / observer / 监控**对 broker 协议 ERROR 的判定:会不会把"broker 配置查询失败"误当成"primary 失联"?如果会,B-broker 类故障会触发不必要的 failover 7. **observer / failover 决策器自身的恢复路径**:如果它有 setup 脚本(比如 Oracle observer 的 setup_observer.sh),脚本里有没有在等某个数据库实例 OPEN?有没有 timeout 兜底?没有的话,instance 不可恢复时 observer 永远起不来 → FSFO 永久 blind 8. **watchdog tick interval 与 FSFO threshold 之间的比例**:决定了 B-instance primary kill 是倾向 self-recovery 还是倾向 FSFO。两者接近时会出现概率性 fire;burn-in 时要显式覆盖这一现象。生产配置应该选定一侧的语义并把两者拉开 +9. **listener / port-listener / 接入层进程是否被 watchdog 覆盖**:很多 addon watchdog 只看引擎主进程(如 Oracle 的 pmon),不监控 listener。listener 死了引擎进程仍在、broker 仍报 SUCCESS、redo 仍流(现存通道),但新连接被拒——这是 silent failure。需要把 listener 列为独立监控点,要么 pgrep 监控 + auto-restart,要么作为独立的 ctr restart trigger ## Burn-in(重复 same-class kill)方法学 @@ -230,6 +233,7 @@ burn-in cycle 之间必要的小停顿(≥30s)用来让 reinstate 完成; 7. **"B-broker instance 没倒,所以没意义"** — 错。B-broker 暴露的是 **failover 决策器的误判设计缺陷**——你能不能区分"primary 失联"和"broker 配置查询失败"。这个区分错了,B-broker 故障会被误升级为不必要的 failover。 8. **"single-shot chaos round 通过 = FSFO 行为已验证"** — 错。FSFO 在 B-instance primary kill 下是概率性的(bimodal 分布)。单次 PASS 只代表你看到了 bimodal 的一侧,必须 ≥3 次 same-class kill 才能观察到分布的全貌。burn-in 是必要的,不是 nice-to-have。 9. **"burn-in 期间从被杀的成员上做 broker 状态轮询"** — 错。被杀的成员上跑的 dgmgrl / broker client 在 ctr restart 期间会返回 stale cached state(甚至连续 60-90s 仍报 SUCCESS),导致测试方法学上漏检 ERROR 窗口。**永远从非本轮被杀的成员上轮询 broker 状态**。多 cycle burn-in 还要在每个 cycle 里重新选轮询点。 +10. **"broker SUCCESS = 集群健康"** — 错。broker SUCCESS 是基于现存 daemon-to-daemon TCP 通道判断的"已知健康",**不**包含"新连接能不能建立"。listener 死了之后 broker 仍然可以保持 SUCCESS 数分钟到任意长,因为它从来不主动 probe listener 是否能接受新连接。chaos 矩阵必须显式测 B-listener 类故障,不能用 broker 状态做单一健康判据。 ## 与其他 chaos / 测试方法论的关系 @@ -251,3 +255,4 @@ burn-in cycle 之间必要的小停顿(≥30s)用来让 reinstate 完成; - Round 11 (B-instance on active FSFO target) — target shift 但 FSFO 不 fire;同 round 3 行为,但走 B 路径快 3 倍 - Round 12 (B-instance on non-target standby) — 无 target shift / 无 FSFO / 无 role change,恢复 ~110s,是 B-instance 位置依赖性中的最小影响基线 - Round 13 (B-instance primary SMON 3-cycle burn-in) — bimodal FSFO 分布直接观察:1/3 cycle self-recovery 赢 / 2/3 cycle FSFO fire;reinstate 在压力下无 degradation;burn-in 轮询点陷阱(从被杀成员上轮询 → stale broker state)暴露 + - Round 14 (B-listener silent failure: tnslsnr kill on non-target standby) — ~6 分钟 observation window 内 broker 始终 SUCCESS / 引擎 alert log 不写一行 / 没有任何自愈机制;F39 暴露 runOracle.sh watchdog 不监控 tnslsnr,需要单独 fix PR diff --git a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md index 5effaef..2082526 100644 --- a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md +++ b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md @@ -239,6 +239,15 @@ poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby │ ├── cycle1/FINDINGS.md │ ├── cycle2/00-summary.txt + 02-poll.txt │ └── cycle3/00-summary.txt + 02-poll.txt +├── chaos-tnslsnr-kill-nontarget-standby-2026-05-15/ # round 14 (B-listener silent failure → F39) +│ ├── 00-pre-state.txt +│ ├── 01-kill-event.txt +│ ├── 02-broker-poll.txt +│ ├── 03-diagnostics.txt +│ ├── 04-state-during-outage.txt +│ ├── 05-long-observation.txt +│ ├── 06-recovery.txt +│ └── FINDINGS.md └── F38-observer-setup-no-timeout/ # latent risk discovered during round 8 └── FINDING.md ``` @@ -294,6 +303,7 @@ oracle-1 当时是 active FSFO target,杀其 SMON:T+10s broker ERROR;T+23s | 13-c1 | B-instance primary SMON burn-in 第 1 次 | **NO (self-recovery 赢)** | NO | n/a | ~75s | | 13-c2 | B-instance primary SMON burn-in 第 2 次 | YES | 0→2 | n/a | ~150s | | 13-c3 | B-instance primary SMON burn-in 第 3 次 | YES | 2→0 | YES (0→1) | ~121s | +| 14 | **B-listener** (tnslsnr kill on non-target standby) | **NO** | NO | NO | **永远 SUCCESS (silent failure)** | ### Round 13 — Burn-in 3 cycles,FSFO 概率性确诊 @@ -323,6 +333,32 @@ oracle-1 当时是 active FSFO target,杀其 SMON:T+10s broker ERROR;T+23s - 想要 "always failover":把 watchdog tick 拉长到 ≥60s(让 self-recovery 总是输 race),或者把 FSFO threshold 缩短到 ≤15s - 想要 "prefer self-recovery if fast":保持当前配置,但要接受 1/3 概率走 self-recovery、2/3 概率走 FSFO 的 bimodal 行为 +### Round 14 — B-listener silent failure (tnslsnr kill on non-target standby) + **F39 watchdog 盲区** + +`chaos-tnslsnr-kill-nontarget-standby-2026-05-15/FINDINGS.md` + +05:29:27Z `kubectl exec o19p15v9-oracle-2 -c oracle -- bash -c 'kill -9 $(pgrep tnslsnr)'`。oracle-2 是非 target standby。 + +**~6 分钟 observation window 全程 broker 报告 SUCCESS,alert log 不写一行**。这是新的故障类(B-listener): +- tnslsnr 死后 2s 内 pgrep 找不到,但 pmon/smon/dmon 完全不变 +- broker 现存 daemon-to-daemon TCP 长连接继续工作,broker config 始终 SUCCESS +- redo 传输(rfs PID:1722)通过现存通道继续 apply +- observer 通过现存连接仍能看到 ORCLCDB_2 健康 +- 但**新连接立刻失败**: `dgmgrl /@ORCLCDB_2` 返回 `ORA-12541: TNS:no listener` +- 没有任何机制触发自愈:runOracle.sh watchdog 只 pgrep `ora_pmon`,不监控 tnslsnr + +**T+377s 手动 `lsnrctl start` 即恢复**。recovery 成本为零(无需 ctr restart)。 + +**F39 (新)**: `runOracle.sh` 的 watchdog(line 572-585)只检查 `ora_pmon_${ORACLE_SID}`。tnslsnr 死了**永远**不被发现,引擎不重启,broker 不报警。生产侧如果 listener OOM / 进程被杀,集群对客户端 "假死"。修复 PR 应该在 watchdog 里加 `pgrep -x tnslsnr` 检查,先尝试 `lsnrctl start` 自愈,失败再 exit 让 ctr restart。 + +**这一类故障的本质特征**: +- 引擎 + broker daemon 都不需要 listener 维持已建立连接 +- 只有"新连接建立"环节需要 listener +- 因此从 cluster 内部观察看不到任何异常 +- 必须用"模拟客户端尝试新连接"的探针才能检测 + +**Methodology 内化**: chaos 矩阵已加 B-listener 子类作为 #11;engine adaptation checklist 已加 listener 监控项;反模式增加"broker SUCCESS = 集群健康"的反例。F39 fix 单独 PR,不混入 PR #129(PR #129 是 chaos 文档)。 + ## 与其他案例 / 方法论的关系 - 方法论母文档:[`../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) — 本案例是它的 Oracle 工程现场补充 From d65528f1de9daa1ec1ff5cda140ef29a0f2a8b84 Mon Sep 17 00:00:00 2001 From: Ava Date: Fri, 15 May 2026 13:51:55 +0800 Subject: [PATCH 8/9] =?UTF-8?q?docs:=20add=20round=2015=20(B-listener=20ki?= =?UTF-8?q?ll=20on=20primary)=20=E2=80=94=20F39=20to=20Sev-1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round 15 lifts round 14's B-listener kill from a non-target standby to the primary. The silent failure pattern reproduces in full: broker SUCCESS for the entire 133s observation window, no role change, no target shift, no FSFO. The new evidence at this position is that the observer reports "Last Ping to Primary: 1 second ago" while any new dgmgrl /@ORCLCDB_0 from another pod returns ORA-12541 immediately — observer and broker keep talking over already-open sessions while every new client connection is rejected. The blast radius is qualitatively different. On a non-target standby (round 14) listener death has minimal cluster-level impact. On the primary (round 15) it is a production-grade application outage with no broker alarm and no auto-recovery. Manual `lsnrctl start` in the oracle ctr restored the cluster in seconds without bouncing anything. That promotes F39 (runOracle.sh watchdog does not pgrep tnslsnr) to a Sev-1 candidate. The fix is cheap — pgrep -x tnslsnr in the same watchdog loop, try lsnrctl start once, exit for ctr restart on failure — and will be filed as a separate engine PR. Case appendix updated with round 15 narrative and file map. Summary table now covers rounds 1-15. Co-Authored-By: Claude Opus 4.7 --- ...pod-kill-vs-engine-internal-crash-guide.md | 1 + ...d-kill-vs-smon-kill-fsfo-asymmetry-case.md | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md index 053201f..a12d4cb 100644 --- a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md +++ b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md @@ -256,3 +256,4 @@ burn-in cycle 之间必要的小停顿(≥30s)用来让 reinstate 完成; - Round 12 (B-instance on non-target standby) — 无 target shift / 无 FSFO / 无 role change,恢复 ~110s,是 B-instance 位置依赖性中的最小影响基线 - Round 13 (B-instance primary SMON 3-cycle burn-in) — bimodal FSFO 分布直接观察:1/3 cycle self-recovery 赢 / 2/3 cycle FSFO fire;reinstate 在压力下无 degradation;burn-in 轮询点陷阱(从被杀成员上轮询 → stale broker state)暴露 - Round 14 (B-listener silent failure: tnslsnr kill on non-target standby) — ~6 分钟 observation window 内 broker 始终 SUCCESS / 引擎 alert log 不写一行 / 没有任何自愈机制;F39 暴露 runOracle.sh watchdog 不监控 tnslsnr,需要单独 fix PR + - Round 15 (B-listener silent failure 升级到 primary 位置) — 与 round 14 同样的静默故障,但应用层等于 production outage(observer "Last Ping to Primary: 1 second ago" 同时新连接 ORA-12541);F39 升级到 Sev-1 候选 diff --git a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md index 2082526..826b74a 100644 --- a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md +++ b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md @@ -248,6 +248,13 @@ poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby │ ├── 05-long-observation.txt │ ├── 06-recovery.txt │ └── FINDINGS.md +├── chaos-tnslsnr-kill-primary-2026-05-15/ # round 15 (B-listener silent failure on primary, F39 Sev-1) +│ ├── 00-pre-state.txt +│ ├── 01-kill-event.txt +│ ├── 02-broker-poll.txt +│ ├── 03-diagnostics.txt +│ ├── 04-recovery.txt +│ └── FINDINGS.md └── F38-observer-setup-no-timeout/ # latent risk discovered during round 8 └── FINDING.md ``` @@ -304,6 +311,7 @@ oracle-1 当时是 active FSFO target,杀其 SMON:T+10s broker ERROR;T+23s | 13-c2 | B-instance primary SMON burn-in 第 2 次 | YES | 0→2 | n/a | ~150s | | 13-c3 | B-instance primary SMON burn-in 第 3 次 | YES | 2→0 | YES (0→1) | ~121s | | 14 | **B-listener** (tnslsnr kill on non-target standby) | **NO** | NO | NO | **永远 SUCCESS (silent failure)** | +| 15 | **B-listener** (tnslsnr kill on **primary**) | **NO** | NO | NO | **永远 SUCCESS (silent failure,但应用层 brick)** | ### Round 13 — Burn-in 3 cycles,FSFO 概率性确诊 @@ -359,6 +367,24 @@ oracle-1 当时是 active FSFO target,杀其 SMON:T+10s broker ERROR;T+23s **Methodology 内化**: chaos 矩阵已加 B-listener 子类作为 #11;engine adaptation checklist 已加 listener 监控项;反模式增加"broker SUCCESS = 集群健康"的反例。F39 fix 单独 PR,不混入 PR #129(PR #129 是 chaos 文档)。 +### Round 15 — B-listener kill on **primary** = 同 silent failure,但应用层风险等级"高" + +`chaos-tnslsnr-kill-primary-2026-05-15/FINDINGS.md` + +05:41:28Z 杀 ORCLCDB_0(primary)的 tnslsnr。轮询从 oracle-1 (ORCLCDB_1, active target) 跑,避开被杀目标。 + +**133s observation window 全程 broker SUCCESS,primary / target 不变,FSFO 不触发**。但应用层影响显著: +- T+285s 跑 `SHOW OBSERVER`:observer 报告 "Last Ping to Primary: 1 second ago" — observer 现存连接 happily ping 着,**完全不知道**新连接已经死了 +- 从 oracle-1 跑新 dgmgrl /@ORCLCDB_0 → ORA-12541: TNS:no listener +- 客户端、监控、新 sqlplus、JDBC 全部无法连接 primary +- primary alert log 在 6 分钟内零新 entry + +T+347s 手动 `lsnrctl start` → 30s 内恢复,无 ctr restart。 + +**生产风险升级**: round 14 是 standby (低影响);round 15 是 primary (**全集群应用层 outage**)。同一类故障在不同位置上严重程度差几个数量级。 + +**F39 升级到 Sev-1 候选**:listener 死 → broker SUCCESS + observer 心跳正常 + 客户写入全部 ORA-12541 → 运维只能等客户告警,分钟到小时级别"假死"。修复 PR 设计已写在 round 14 FINDINGS:watchdog 加 `pgrep -x tnslsnr` 检查,先尝试 `lsnrctl start` 自愈,失败才 exit 触发 ctr restart。 + ## 与其他案例 / 方法论的关系 - 方法论母文档:[`../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) — 本案例是它的 Oracle 工程现场补充 From 0ca57f9c83f61d41e857f442d77f1fcf3ab6e259 Mon Sep 17 00:00:00 2001 From: Ava Date: Fri, 15 May 2026 14:10:27 +0800 Subject: [PATCH 9/9] docs: add round 16 (B-listener on active target) + address review blockers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Round 16 收口 B-listener position-axis matrix (non-target / primary / active FSFO target 三个位置均 silent failure, F39 position-independent) - 案例附录 intro 补 'Affected by version skew' 字段 + 案例 scope 行 (Oracle 19c / KB 1.0.3-beta.x / 单 cluster / round 1-16 only) - 方法论 doc 把 version skew 从 '不受影响' 改为 'yes — engine 版本 / HA 模式 / observer config / watchdog 实现 / KB sidecar 都改变观察结果' - 把案例 evidence 绝对路径 /Users/wei/... 改为 repo-relative 引用 - 悬空链 (oracle-f35-sync-membership-grace-period-case.md / addon-multi-container-shared-file-symlink-write-guide.md) 改为 '待对应文档 land 后回填链接' 描述, 避免悬空 - SKILL-INDEX.md 新增两篇 (chaos pod-kill vs engine internal crash methodology + Oracle chaos case) 到 section 2 + 文档全列表 + Oracle 案例区 - 添加 round 16 到方法论 doc 案例附录摘要列表 --- docs/SKILL-INDEX.md | 3 ++ ...pod-kill-vs-engine-internal-crash-guide.md | 5 +- ...d-kill-vs-smon-kill-fsfo-asymmetry-case.md | 46 +++++++++++++++++-- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/docs/SKILL-INDEX.md b/docs/SKILL-INDEX.md index 5054847..7df5f9c 100644 --- a/docs/SKILL-INDEX.md +++ b/docs/SKILL-INDEX.md @@ -48,6 +48,7 @@ - [`addon-resource-rich-cluster-parallel-test-guide.md`](addon-resource-rich-cluster-parallel-test-guide.md) — idc / idc1 / idc2 / idc4 这类资源充足集群上的并行测试调度方法:先测容量、按 suite lane 分批、逐步提升 N、namespace / evidence / cleanup 隔离、把环境压力和产品失败分开报告;KBE / KB runtime 在这些 IDC 环境中必须以 vcluster 为 SUT,host k8s 只承载 runner / helper / bootstrap;确认环境问题后通过 @Musk3 找飞书用户李国银;配套 skill `parallel-test-execution` - [`addon-multi-ns-registry-scan-preflight-guide.md`](addon-multi-ns-registry-scan-preflight-guide.md) — 多 namespace / 多 topology 并发测试或 chaos suite 启动前把测试 scope 拆成 **verified scope vs scan-only future-gate** 两层:本轮跑过的 topology 写 verified 结论;未跑但 pre-flight scan 命中 docker.io 漏点的 topology 写 future-gate precondition。具体 application 是审计 live `ComponentVersion` + `ParametersDefinition.toolsSetup.toolConfigs[].image` 的 image source 一致性。N=2 MySQL grounded(task #5 functional 多 ns 并发 + task #6 chaos vcluster pre-patch) - [`addon-soak-test-result-classification-guide.md`](addon-soak-test-result-classification-guide.md) — 长跑型测试(24h+ soak / chaos / fault-injection)出结果之后的 4-state 结果分类方法论:`invariant-break` / `product-path-failure` / `harness-race` / `external-environmental-cascade`。Q1-Q5 决策树(mermaid)+ N≥2 自验证最小证据门槛(harness-race / cascade 必须有同类 Succeed 或 baseline ±1σ 对照样本)+ ACCEPTED 判据(`invariant-break = 0 AND product-path-failure = 0`,**不是** fault total = 0)+ N=3/2 grounded 案例对照(CH30 harness-race + CH20 external-env-cascade)+ AG quorum non-sticky 行为附注。与 [`addon-test-acceptance-and-first-blocker-guide.md`](addon-test-acceptance-and-first-blocker-guide.md) 单次 fail 分层方法论互补(前者多次注入聚合维度) +- [`addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) — 同一引擎 "崩溃"分三种形态(K8s 层 pod-kill / 引擎 instance critical 进程崩溃 / 引擎 broker 辅助进程崩溃),三类走完全不同的恢复路径并可能触发完全不同的 failover 行为,必须分别测。文档给三轴 chaos 矩阵 + B 类下再分 instance / broker(master, worker) / listener 三子轴 + burn-in(同位置同类故障 ≥3 次) + 位置依赖性矩阵(primary vs active FSFO target vs non-target standby)+ 跨引擎适配 checklist + 反模式表;Oracle 案例见 `cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md` ### 3. 环境 ready 前 / 环境层撞坑 @@ -166,6 +167,7 @@ - [`docs/addon-pr-reviewer-routing-guide.md`](addon-pr-reviewer-routing-guide.md) — addon 代码 PR reviewer routing 方法论:先查当前 PR touched files、初版 PR、addon 目录主要作者、最近相关功能作者,再按"最近功能维护者 → 初版 / 主要作者 → addon owner → fallback reviewer → 带证据问 Weston"选择 reviewer。包含 @Musk3 安全转发格式、Kevin 汇总表、Oracle PR #1276 / PR #764 反例,配套 callable skill `addon-pr-reviewer-routing` - [`docs/addon-github-submission-discipline-guide.md`](addon-github-submission-discipline-guide.md) — 多 agent 协作 + GitHub 公开仓库的边界纪律:(1) AI provenance trailer(`Co-Authored-By: Claude` / `🤖 Generated with` / `noreply@anthropic.com`)不外漏的硬规则与兜底命令链(heredoc + `git commit --amend -m "$(... | sed)"` strip / push 前 grep 自检);(2) 多 agent 并发推同一 PR branch 的 cascade 事故响应 playbook(force-with-lease lemma:lease 锚 last-fetched remote tip 不防 fetch 后并发 push / 双向 `git log --oneline` ritual / dropped-commit owner self-recover / single-owner-execute 收口)。5 条 doctrine(A force-with-lease / B per-commit grep / C cascade single-owner-execute / D forensic 自查 / E content-delta verify)+ §5 cross-cutting rules(forensic self-review / Doctrine E shorthand / evidence-post obligation / 递归 self-application) - [`docs/addon-soak-test-result-classification-guide.md`](addon-soak-test-result-classification-guide.md) — 长跑型测试(24h+ soak / chaos / fault-injection)出结果之后的结果分类方法论。**核心 framing**:fault total / PASS-FAIL 计数无法回答"是否 ACCEPTED",必须把每条 fault 注入按"哪一层先失败"落到 4-state schema:(1) `invariant-break`(不变量破坏 → ROLLBACK)/ (2) `product-path-failure`(产品恢复路径失败)/ (3) `harness-race`(测试工具时序竞争)/ (4) `external-environmental-cascade`(外部环境级联)。**判据**:Q1(bad_ack > 0)→ Q2(cluster 终态)→ Q3(OpsRequest Failed + N≥2 自验证)→ Q4(duration 超 mean+3σ + 外部事件关联 + 对照样本)→ product-pass(mermaid 流程图)。**N≥2 自验证最小证据门槛**:harness-race 需同类 Succeed 对照、cascade 需 baseline ±1σ 对照样本;单 sample 不能下"非产品"结论。**ACCEPTED 判据**:`invariant-break = 0 AND product-path-failure = 0`,harness-race / cascade 不阻塞但触发对应修复 ticket。grounded N=3 CH30 harness-race 对照(fault-026 vs 029/033)+ N=2 CH20 cascade negative-control(fault-028 21min outlier vs 031 95s 1σ 内)+ AG quorum non-sticky 3-transition 行为附注。与 [`addon-test-acceptance-and-first-blocker-guide.md`](addon-test-acceptance-and-first-blocker-guide.md) 单次 fail first-blocker 分层方法论形成互补对子(前者聚合维度,后者单次维度) +- [`docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) — Addon chaos test 设计原则:同一 DB 实例的"崩溃"有三种**完全不同**的故障形态 — (A) K8s 层 pod-kill / 节点宕机;(B-instance) 引擎 critical 进程崩溃(Oracle SMON/LGWR / PG postmaster),引擎自行终止 instance;(B-broker) 仅 HA / 复制 / 监控相关进程崩溃(Oracle DMON/INSV/NSV* / MySQL semi-sync 线程),引擎悄悄重启该进程,instance / 容器不动。三类**走完全不同的恢复路径**并**可能触发完全不同的 failover 行为**,一类测了不等于测了另一类。文档给:双轴 / 三轴 chaos 矩阵、B 类下 instance vs broker(master, worker) vs listener 三子轴、burn-in 方法学(≥3 cycle / poll point 解耦 / topology rotation)、FSFO bimodal probabilism 段、位置依赖性矩阵(primary vs active FSFO target vs non-target standby)、跨引擎适配 checklist(含 listener watchdog 覆盖)、10 条反模式(含"single-shot ≠ verified" / "broker SUCCESS ≠ cluster healthy" / "不要从被杀成员 poll broker")。Oracle 19c grounded:rounds 1–16 见 `cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md` ## 案例材料 @@ -197,6 +199,7 @@ - [`docs/cases/oracle/oracle-chart-vs-kb-schema-skew-multi-stage-case.md`](cases/oracle/oracle-chart-vs-kb-schema-skew-multi-stage-case.md) — Oracle 1.0.0-alpha.0 chart 在已发布 KB 装不上的三段反转:先误判「整代代差」、再误判「chart 字段路径错位」、最后查清是「chart 跟 KB main 上未发布 API(PR #10100 / #10109)」。同仓 `release-1.0` 分支才是答案。属 [`addon-chart-vs-kb-schema-skew-diagnosis-guide.md`](addon-chart-vs-kb-schema-skew-diagnosis-guide.md) 的工程现场补充 - [`docs/cases/oracle/oracle-12c-post-switchover-probe-cascade-kill.md`](cases/oracle/oracle-12c-post-switchover-probe-cascade-kill.md) — reconfigure_deep Run 1→3 闭环:Bug #12 (DBCA 跑期间 liveness 误杀,initialDelay=600 + 90s 重启窗口) + Bug #13 (post-switchover 慢控制面 flap readiness);3 layer fix(cmpd probe 参数 + liveness.sh 软失败 + checkDBStatus.sh best-effort dgmgrl);Run 3 全 PASS + RESTARTS=0 实证;属 [`addon-probe-timeout-and-soft-failure-guide.md`](addon-probe-timeout-and-soft-failure-guide.md) 工程现场补充 - [`docs/cases/oracle/oracle-12c-processes-cue-paramdef-range-case.md`](cases/oracle/oracle-12c-processes-cue-paramdef-range-case.md) — reconfigure_deep T22d FAIL:`processes: int & >=6` cue 太宽,10 通过 ValidatePhase → ORA-603/1092 → instance terminated → KB OpsRequest 卡 Running 25min+;fix `>=100` Run 3 验证生效(ValidatePhase reject within 10s);属 [`addon-paramdef-cue-range-validation-guide.md`](addon-paramdef-cue-range-validation-guide.md) 工程现场补充 +- [`docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c grounded chaos 案例库(`o19p15v9` cluster, MaxPerformance + ASYNC + FSFO threshold 30s):rounds 1-16 完整记录三轴 chaos(A pod-kill / B-instance SMON+LGWR / B-broker DMON+INSV / B-listener tnslsnr) × 三位置(primary / active FSFO target / non-target standby)+ burn-in(round 13)+ A+B-instance concurrent race (round 8)。**关键 finding**:(1) A vs B-instance 在 primary 上行为根本不同(A 走 pod recreate 自愈, B 走 FSFO failover),(2) FSFO trigger 是 bimodal probabilism(watchdog tick 相位 vs FSFO threshold 决定 fire 还是 self-recover),(3) F39 — `runOracle.sh` watchdog blind to tnslsnr loss → listener 死后 broker SUCCESS + observer 心跳正常 + 新客户连接全部 ORA-12541 → 无 watchdog 不自愈(Sev-1,PR #1320),(4) F38 — `setup_observer.sh` 无 timeout 等 instance OPEN + hardcode `${ORACLE_SID}_0`,primary 永久不恢复时 observer 永远起不来。属 [`addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) 工程现场补充 ### OceanBase diff --git a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md index a12d4cb..2422aaa 100644 --- a/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md +++ b/docs/addon-chaos-pod-kill-vs-engine-internal-crash-guide.md @@ -4,7 +4,7 @@ > **Status**: stable > **Applies to**: any KB addon with HA/failover engine(MySQL / Oracle / PostgreSQL / Redis / MongoDB 等) > **Applies to KB version**: any -> **Affected by version skew**: 不受 KB 版本影响 — 本文是 chaos test 设计原则,与 addon image 版本无关 +> **Affected by version skew**: yes — 方法学(双轴 / 三轴 + 矩阵)跨版本稳定,但具体观测结论与时序受多层版本影响:engine 版本、HA 模式(MaxPerf / MaxAvail)、observer / broker config、container 内 watchdog 实现、K8s 重启路径、KB sidecar 行为 都会改变 FSFO 触发概率与恢复时长。引用具体案例时必须把这些 envelope 字段一并落定,不能直接把"我环境 N=1 观测到"当成"任何环境一样" ## 先用白话理解这篇文档 @@ -245,7 +245,7 @@ burn-in cycle 之间必要的小停顿(≥30s)用来让 reinstate 完成; 引擎相关的实测命令、日志样本、recovery 时间数据放在独立案例材料里: -- [`cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c on KubeBlocks o19p15v9 cluster: 13 轮 chaos 矩阵实测(其中 round 13 包含 3 cycle burn-in),覆盖三类故障的对比 + 完整 alert log + observer log + broker poll 轨迹 + burn-in 概率分布。其中: +- [`cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md`](cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md) — Oracle 19c on KubeBlocks o19p15v9 cluster: 16 轮 chaos 矩阵实测(其中 round 13 包含 3 cycle burn-in),覆盖四类故障(A / B-instance / B-broker / B-listener)的对比 + 完整 alert log + observer log + broker poll 轨迹 + burn-in 概率分布 + position-axis matrix。其中: - Round 1 (A 类 primary pod-kill) — FSFO 因 standby SUSPEND 抑制 - Round 6 (B-instance SMON kill) — FSFO 触发,role 切换,自动 reinstate - Round 7 (B-instance LGWR kill) — 验证 B-instance 行为不止 SMON @@ -257,3 +257,4 @@ burn-in cycle 之间必要的小停顿(≥30s)用来让 reinstate 完成; - Round 13 (B-instance primary SMON 3-cycle burn-in) — bimodal FSFO 分布直接观察:1/3 cycle self-recovery 赢 / 2/3 cycle FSFO fire;reinstate 在压力下无 degradation;burn-in 轮询点陷阱(从被杀成员上轮询 → stale broker state)暴露 - Round 14 (B-listener silent failure: tnslsnr kill on non-target standby) — ~6 分钟 observation window 内 broker 始终 SUCCESS / 引擎 alert log 不写一行 / 没有任何自愈机制;F39 暴露 runOracle.sh watchdog 不监控 tnslsnr,需要单独 fix PR - Round 15 (B-listener silent failure 升级到 primary 位置) — 与 round 14 同样的静默故障,但应用层等于 production outage(observer "Last Ping to Primary: 1 second ago" 同时新连接 ORA-12541);F39 升级到 Sev-1 候选 + - Round 16 (B-listener kill on active FSFO target) — 收口 position-axis matrix:与 round 14 (non-target) / 15 (primary) 同样 silent failure;得到结论"F39 是 position-independent";对比 B-instance 位置矩阵的位置-依赖差异,说明位置矩阵本身就是 chaos 设计的关键信号 diff --git a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md index 826b74a..80d6e7e 100644 --- a/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md +++ b/docs/cases/oracle/oracle-chaos-pod-kill-vs-smon-kill-fsfo-asymmetry-case.md @@ -3,7 +3,10 @@ > **Audience**: Oracle addon dev / test,以及任何 HA 引擎团队想了解 chaos 注入层级差异如何改变 failover 决策 > **Status**: stable > **Applies to**: Oracle 19c on KubeBlocks(方法论参见 [`../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md)) -> **Applies to KB version**: 验证于 KB 1.0.3-beta.x(与具体 KB 版本无关 — 现象由 Oracle DG broker + K8s 重启路径决定) +> **Applies to KB version**: 验证于 KB 1.0.3-beta.x +> **Affected by version skew**: yes — 现象由 Oracle 19c DG broker 行为 + K8s pod 重启路径 + addon `runOracle.sh` 当前 watchdog 实现 三者共同决定。换 Oracle 版本(12c / 23ai)、换 HA 模式(MaxAvailability)、换 observer config、换 KB sidecar 行为、修 watchdog 任一项都会改变观察结果 +> +> **案例范围(scope)**:Oracle 19c / KB 1.0.3-beta.x / 一个 cluster (`o19p15v9` on idc2) / 本案例记录的 round 1–16 实测;其余 envelope(其他 Oracle 版本、其他 HA 模式、其他 KB 版本)未跑,不做断言。 本案例是 [`../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) 的工程现场补充。在同一个 Oracle 19c 三节点集群(MaxPerformance + ASYNC + FSFO threshold 30s)上跑同一组配置,仅故障注入层级不同 — 一次是 K8s 层 pod-kill,一次是 Oracle 引擎内部 SMON SIGKILL — FSFO 出现了完全相反的结果。 @@ -17,7 +20,7 @@ - **FSFO**:Enabled in Potential Data Loss Mode,threshold = 30s,AutoReinstate = TRUE - **CommunicationTimeout**:180s - **Active Target(pre-chaos)**:round 1 时为 ORCLCDB_1;round 6 时为 ORCLCDB_1(经过 round 3/4/5 后又重新对齐) -- **Evidence path**:`/Users/wei/ApeCloud/evidence-skyworth-oracle-19c/chaos-primary-kill-2026-05-15/` 与 `chaos-smon-kill-primary-2026-05-15/` +- **Evidence**:本地归档目录 `evidence-skyworth-oracle-19c/chaos-primary-kill-2026-05-15/` 与 `chaos-smon-kill-primary-2026-05-15/`(按 round 分目录,不进公开 repo;如需复核请联系本案例 maintainer) ## Round 1 — K8s 层 pod-kill on primary @@ -208,7 +211,7 @@ poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby ## 案例文件清单 ``` -/Users/wei/ApeCloud/evidence-skyworth-oracle-19c/ +evidence-skyworth-oracle-19c/ # local archive root; not in repo ├── chaos-primary-kill-2026-05-15/ # round 1 (A) │ ├── FINDINGS.md │ ├── during/00-kill-event.txt @@ -255,6 +258,14 @@ poll 7 (T+150s) status=SUCCESS, ORCLCDB_0 已 reinstate 成 standby │ ├── 03-diagnostics.txt │ ├── 04-recovery.txt │ └── FINDINGS.md +├── chaos-tnslsnr-kill-active-target-2026-05-15/ # round 16 (B-listener on active FSFO target, F39 position-axis matrix close) +│ ├── setup.txt +│ ├── t0.txt +│ ├── t_recovery.txt +│ ├── 01-kill.txt +│ ├── 02-recovery.txt +│ ├── 03-broker-final.txt +│ └── FINDINGS.md └── F38-observer-setup-no-timeout/ # latent risk discovered during round 8 └── FINDING.md ``` @@ -385,8 +396,35 @@ T+347s 手动 `lsnrctl start` → 30s 内恢复,无 ctr restart。 **F39 升级到 Sev-1 候选**:listener 死 → broker SUCCESS + observer 心跳正常 + 客户写入全部 ORA-12541 → 运维只能等客户告警,分钟到小时级别"假死"。修复 PR 设计已写在 round 14 FINDINGS:watchdog 加 `pgrep -x tnslsnr` 检查,先尝试 `lsnrctl start` 自愈,失败才 exit 触发 ctr restart。 +### Round 16 — B-listener kill on **active FSFO target standby** = position-axis matrix 收口 + +`chaos-tnslsnr-kill-active-target-2026-05-15/FINDINGS.md` + +05:59:13Z 杀 oracle-1(ORCLCDB_1,active FSFO target)的 tnslsnr。轮询从 primary oracle-0 跑,避开被杀目标。 + +**5min observation window 全程 broker SUCCESS,Active Target 停留在 ORCLCDB_1,FSFO 不触发**。同 round 14 / 15 silent failure: +- T+5s: `pgrep -x tnslsnr` 找不到,pmon/smon 全活 +- T+15s: broker `Configuration Status: SUCCESS (status updated 1 second ago)` +- T+26s: observer "Last Ping to Target: 2 seconds ago"(现存 TCP),但新 `sqlplus @ORCLCDB_1` 立即 ORA-12541 +- T+3m51s: broker 仍 SUCCESS,Active Target 仍 ORCLCDB_1,alert log 自 T0 起零新 entry(最近一条 05:17 早已不相关) +- T_recovery(T+5m20s): `lsnrctl start` → 新 tnslsnr pid 20100,broker 仍 SUCCESS,无 ctr restart + +**B-listener 位置依赖性矩阵收口**: + +| round | victim 位置 | broker SUCCESS? | FSFO fire? | target shift? | observer 视角 | 新连接到 victim | +|---|---|---|---|---|---|---| +| 14 | non-target standby (ORCLCDB_2) | YES | NO | NO | n/a | ORA-12541 | +| 15 | primary (ORCLCDB_0) | YES | NO | NO | "Last Ping to Primary: 1s ago" | ORA-12541 | +| 16 | active FSFO target (ORCLCDB_1) | YES | NO | NO | "Last Ping to Target: 2s ago" | ORA-12541 | + +**核心结论**: F39(watchdog 盲于 tnslsnr)**与位置无关** —— 三个位置上同样 silent,同样不自愈,同样依赖手动 `lsnrctl start`。这就是为什么 fix 必须从 watchdog 层(uniform 解)做,而不是任何位置专属的 fallback。 + +对照 B-instance 位置矩阵(rounds 6/11/12)—— 那里 primary vs active target vs non-target 会产生根本不同的 FSFO 行为(fire vs target-shift vs no-op)—— B-listener 的位置矩阵反而**全同**。两类故障的位置依赖性差异本身就是 chaos 测试设计的关键信号:位置-依赖的 fault class 需要 per-position 测试,位置-无关的 fault class 测一处就够但必须确认它真的位置无关。 + +**与 F39 fix PR 关系**: round 16 是 fix 提交后的最后一次 pre-fix 验证;提供同一引擎实现下三个位置一致的 baseline,PR #1320 验证时只需在一个位置上观察 watchdog 自愈即可。 + ## 与其他案例 / 方法论的关系 - 方法论母文档:[`../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md`](../../addon-chaos-pod-kill-vs-engine-internal-crash-guide.md) — 本案例是它的 Oracle 工程现场补充 - 同 cluster 上 round 3/4/5 的其他 chaos 轮次:见 `evidence-skyworth-oracle-19c/chaos-{fsfo-target-kill,observer-kill,dual-standby-kill}-*` 各自 FINDINGS.md -- F34 / F35 / F36 / F37 多容器共享文件相关 finding:见 [`oracle-f35-sync-membership-grace-period-case.md`](oracle-f35-sync-membership-grace-period-case.md) 与 [`../../addon-multi-container-shared-file-symlink-write-guide.md`](../../addon-multi-container-shared-file-symlink-write-guide.md) +- F34 / F35 / F36 / F37 多容器共享文件相关 finding:当前在本地 evidence 归档 `evidence-skyworth-oracle-19c/F34-*` ~ `F37-*`,等对应方法论 / 案例文档 land 后再回填本节链接(避免悬空链)