# ISS-0101 Plan: 诊断缺少 TiKV Raft "peer became inactive" 等终止原因日志证据

## Background

GitHub issue #624: O11Y-2579585 用户反馈 — "I want the reason of termination. I believe some
network jitter happened. Find me evidence such as 'peer became inactive'"

- Workspace: ws-1780299941478-b19ee2680 (prod, investibook.tidbcloud.com)
- Ticket: O11Y-2579585
- User: yishuai.li@pingcap.com

用户的核心投诉：
1. Agent 未能找到/展示节点终止原因（termination reason）
2. 用户怀疑是 network jitter，期望 Agent 提供 TiKV Raft 日志中的具体证据，如 "peer became inactive"

## What Went Wrong

当节点重启/终止时，Agent 当前的诊断流程（ISS-0038/ISS-0039 修复后）会检查：
- component_health（实例数量变化）
- Activity Hub 事件（是否有计划内操作）
- 内存指标（是否 OOM）

但**缺少对 TiKV Raft 层日志的主动搜索**，包括：
- `"peer became inactive"` — 表示某 peer 长时间未响应心跳，被认为不活跃
- `"unreachable message"` — 网络无法到达 peer
- `"leader stepped down"` — 网络分区或选举故障
- `"failed to send heartbeat"` — 心跳发送失败，网络抖动典型表现
- `"slow tick"` — Raft tick 延迟，可能由网络延迟引起
- `"raft: %d is unreachable"` — TiKV Raft 状态机判断 peer 不可达
- `"store heartbeat"` — store 心跳相关

这些日志是网络抖动（network jitter）→ TiKV peer 断开 → 节点重启/终止因果链的直接证据。
当前 Loki 日志查询（collect_evidence.py A6 段）仅查询通用 ERROR/FATAL 日志，
未针对 Raft 层专项搜索。

## Observed Gap

在 node restart/termination 类告警中，Agent 无法回答：
- "是网络抖动还是 OOM 还是计划重启？"
- "有没有 peer became inactive 的日志？"

即使 Activity Hub 确认了非计划操作（无 ModifyClusterSize 等事件），Agent 也无法区分：
- OOM Kill（内存不足）
- Raft 层故障（网络/心跳超时）
- 其他原因（磁盘满、进程崩溃）

## Root Cause Analysis

诊断流程缺少**终止原因分类步骤**（Termination Reason Classification）：

当前流程在 component_count 下降/restart 时：
1. 检查 Activity Hub（是否计划操作）✓
2. 检查内存指标（是否 OOM）✓
3. **缺少：搜索 TiKV Raft 日志中的网络相关模式** ✗
4. **缺少：结构化输出终止原因分类** ✗

## Fix Plan

### Step 1: 在 collect_evidence.py 中增加 TiKV Raft 日志专项查询

在 A6 段（logs）之后新增 A6b 段（raft_termination_logs），仅在检测到节点重启/终止时触发：

```python
# A6b: TiKV Raft termination pattern search (only when component_health shows restarts)
RAFT_PATTERNS = [
    "peer became inactive",
    "unreachable message",
    "leader stepped down",
    "failed to send heartbeat",
    "slow tick",
    "is unreachable",
    "store heartbeat",
    "raft tick timeout",
]
for pattern in RAFT_PATTERNS:
    # Loki query: {job="tikv"} |= "peer became inactive"
    results = clinic_loki_query(cluster_id, f'{{job="tikv"}} |= "{pattern}"',
                                start=window_start, end=window_end)
    if results:
        # write to raft_termination_evidence.json
```

### Step 2: 在 SKILL.md 中增加终止原因分类规则

在 Node C（证据分析）中增加：

```
当诊断发现节点重启（restart_count > 0 或 component_count 下降）时，必须对终止原因分类：

终止原因判断优先级：
1. 计划内操作：Activity Hub 中有 ModifyClusterSize/ScaleResourcePool 事件 → 标注为"计划内扩缩容"
2. OOM Kill：内存在重启前接近 node limit（>85%），且有 OOMKilled/SIGKILL 信号 → 标注为"OOM"
3. 网络分区/Raft 故障：TiKV Raft 日志中有 "peer became inactive"/"unreachable" 等 → 标注为"Raft 层故障/网络抖动"
4. 未知：以上均无证据 → 明确标注"终止原因未能确认，建议提供更多日志"

对用户提到的具体日志 pattern（如 "peer became inactive"），Agent 必须主动在 Loki 日志中搜索
并展示结果（找到或未找到均需说明）。
```

### Step 3: 当用户提示特定日志 pattern 时，Agent 应立即执行专项搜索

在 intake_context 阶段增加规则：
- 若用户消息中包含引号内的日志片段（如 "peer became inactive"），Agent 应立即将其作为
  Loki 搜索关键词执行查询，而非等到完整的证据采集流程
- 用户提供的日志 pattern 是最高优先级的搜索线索

## Verification

修复后验证：
1. 对 O11Y-2579585 重新运行诊断，检查 Agent 是否：
   - 主动在 TiKV Loki 日志中搜索 "peer became inactive"
   - 正确分类终止原因（网络抖动/OOM/计划操作/未知）
   - 若搜索到日志，引用具体时间和内容；若未搜索到，明确说明
2. 对其他 node restart 案例（如 TCOC-5140, O11Y-2489480）回归验证终止原因分类正确
