---
phase: 03-hotpath
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/sqllog.rs
autonomous: true
requirements:
  - HOT-01
  - HOT-02

must_haves:
  truths:
    - "find_indicators_split 在记录末尾不以 '.' 结尾时不执行任何 rfind/memrchr 调用，直接返回 len"
    - "find_indicators_split 使用单次 memrchr(b':') 反向扫描替代 3 次独立 FinderRev::rfind 调用"
    - "所有现有测试全部通过（cargo test exits 0）"
    - "静态 FINDER_REV_EXECTIME / FINDER_REV_ROWCOUNT / FINDER_REV_EXEC_ID 已从 sqllog.rs 删除"
  artifacts:
    - path: "src/sqllog.rs"
      provides: "优化后的 find_indicators_split，包含 HOT-01 早退和 HOT-02 单次反向扫描"
      contains: "last_meaningful != Some(b'.')"
    - path: "src/sqllog.rs"
      provides: "移除了 3 个 FINDER_REV_* LazyLock 静态变量"
      contains: "memrchr::memrchr"
  key_links:
    - from: "src/sqllog.rs :: find_indicators_split"
      to: "parse_indicators_from_bytes"
      via: "CORR-03 验证守卫保留不变"
      pattern: "parse_indicators_from_bytes.*is_none"
---

<objective>
对 `find_indicators_split` 实施两项互不干扰的热路径微优化：

1. **HOT-01 早退**：在所有 rfind/memrchr 扫描之前检查 content_raw 末尾字节，若不以 `'.'` 结尾则立即返回 len，跳过 INDICATORS_WINDOW 内的所有搜索。
2. **HOT-02 单次反向扫描**：将 3 次独立的 `FinderRev::rfind` 调用替换为单次 `memrchr(b':')` 反向扫描，每次命中后用 `ends_with` 检查关键字前缀，同时删除不再需要的 `FINDER_REV_*` 静态变量。

Purpose: 减少无指标记录（大多数日志记录）的热路径开销；减少有指标记录的 SIMD 启动次数。
Output: 修改后的 `src/sqllog.rs`，HOT-01/02 均生效，CORR-03 验证守卫保持原位。
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/03-hotpath/03-RESEARCH.md

<interfaces>
<!-- 当前 find_indicators_split 实现（src/sqllog.rs:227-252），executor 需完整替换此函数 -->

```rust
// 当前静态变量（HOT-02 后需全部删除）
static FINDER_REV_EXECTIME: LazyLock<FinderRev<'static>> =
    LazyLock::new(|| FinderRev::new(b"EXECTIME: "));
static FINDER_REV_ROWCOUNT: LazyLock<FinderRev<'static>> =
    LazyLock::new(|| FinderRev::new(b"ROWCOUNT: "));
static FINDER_REV_EXEC_ID: LazyLock<FinderRev<'static>> =
    LazyLock::new(|| FinderRev::new(b"EXEC_ID: "));

// 当前 find_indicators_split（sqllog.rs:227-252）
fn find_indicators_split(&self) -> usize {
    let data = &self.content_raw;
    let len = data.len();
    let start = len.saturating_sub(INDICATORS_WINDOW);
    let window = &data[start..];

    let mut earliest = window.len();
    for finder in [
        &*FINDER_REV_EXECTIME,
        &*FINDER_REV_ROWCOUNT,
        &*FINDER_REV_EXEC_ID,
    ] {
        if let Some(idx) = finder.rfind(window) {
            earliest = earliest.min(idx);
        }
    }
    let split = start + earliest;
    if split < len && parse_indicators_from_bytes(&data[split..]).is_none() {
        return len;
    }
    split
}

// CORR-03 验证守卫（必须保留，在 HOT-02 扫描循环退出后调用）
// parse_indicators_from_bytes(&data[split..]).is_none() → return len

// memchr crate 已在 sqllog.rs use 中（memchr::memchr），需额外导入 memchr::memrchr
use memchr::memrchr;
```

关键字长度常量（用于 HOT-02 计算 kw_start）：
- `b"EXECTIME"` → 8 字节
- `b"ROWCOUNT"` → 8 字节
- `b"EXEC_ID"` → 7 字节
</interfaces>
</context>

<tasks>

<task type="auto" tdd="true">
  <name>Task 1: HOT-01 早退逻辑</name>
  <files>src/sqllog.rs</files>
  <read_first>
    - src/sqllog.rs（必读：了解当前 find_indicators_split 实现及 use 语句，行 1-30 和 227-252）
    - .planning/phases/03-hotpath/03-RESEARCH.md（Pitfall 1：末尾换行符 \n/\r 处理）
  </read_first>
  <behavior>
    - Test 1: 末尾为 `b'.'`（含换行）的记录不被早退，继续执行后续搜索逻辑
    - Test 2: 末尾为 `b'\n'` 的无指标记录（如纯 SQL）被早退，返回 len
    - Test 3: 末尾为 `b'.'` 但无真实指标的记录（如 SQL 以 `.` 结尾）不被早退，CORR-03 守卫最终返回 len
    - Test 4: 空 content_raw（len == 0）早退返回 0（即 len）
  </behavior>
  <action>
在 `find_indicators_split` 函数体开头（`let start = ...` 之前）插入 HOT-01 早退块：

```rust
fn find_indicators_split(&self) -> usize {
    let data = &self.content_raw;
    let len = data.len();

    // HOT-01: O(1) 早退 — DM 格式中有指标的记录必以 '.' 结尾（EXEC_ID: N.）
    // 跳过末尾 \n/\r，取最后一个有效字节；非 '.' 则无指标，直接返回
    let last_meaningful = data
        .iter()
        .rev()
        .find(|&&b| b != b'\n' && b != b'\r')
        .copied();
    if last_meaningful != Some(b'.') {
        return len;
    }

    // 余下逻辑不变（本任务保留原有 FINDER_REV_* 搜索，HOT-02 在 Task 2 替换）
    let start = len.saturating_sub(INDICATORS_WINDOW);
    ...
}
```

注意：本任务**只加早退块**，不修改早退之后的搜索逻辑（Task 2 负责替换）。

为以下 4 个行为补充测试（若测试文件中尚无对应 case）：
- 在 `tests/` 下找到覆盖 `find_indicators_split` 或 `parse_performance_metrics` 的测试文件
- 添加 `hot01_early_exit_no_dot_suffix`：记录末尾为 SQL 文本（无 `.` 结尾），验证 `sql == content_raw` 全文（无截断）
- 添加 `hot01_early_exit_empty_content`：content_raw 为空字节，验证 body_len() == 0
  </action>
  <verify>
    <automated>cargo test 2>&1 | tail -5</automated>
  </verify>
  <done>
    - `cargo test` 全部通过
    - `src/sqllog.rs` 中 `find_indicators_split` 开头包含 `last_meaningful != Some(b'.')`
    - 早退块位于 `let start = len.saturating_sub` 之前
  </done>
</task>

<task type="auto" tdd="true">
  <name>Task 2: HOT-02 单次反向字节扫描替换 3 次 FinderRev</name>
  <files>src/sqllog.rs</files>
  <read_first>
    - src/sqllog.rs（必读：确认 Task 1 已完成，当前 find_indicators_split 已有早退块）
    - .planning/phases/03-hotpath/03-RESEARCH.md（HOT-02 Pattern 章节和 Pitfall 2/3）
  </read_first>
  <behavior>
    - Test 1: 含完整指标（EXECTIME + ROWCOUNT + EXEC_ID）的记录，split 点正确（与原 3 次 rfind 结果相同）
    - Test 2: SQL body 内含 `EXECTIME:` 假关键字 + 真实指标，split 返回真实指标起始（最左匹配）
    - Test 3: SQL body 内含多个 `:` 字符（如 URL），不影响 split 结果
    - Test 4: 仅有 EXEC_ID 无 EXECTIME/ROWCOUNT 的记录，split 正确
  </behavior>
  <action>
将 HOT-01 早退块之后的搜索逻辑完整替换为单次 memrchr 扫描：

1. 在文件顶部 use 语句中添加 `memrchr::memrchr`（检查是否已有 `use memchr::memrchr;` 或通过 `memchr::memrchr` 限定路径调用）。

2. 删除 3 个 `FINDER_REV_*` 静态变量（第 24-29 行）：
   ```rust
   // 删除以下 3 个 LazyLock
   static FINDER_REV_EXECTIME: ...
   static FINDER_REV_ROWCOUNT: ...
   static FINDER_REV_EXEC_ID: ...
   ```

3. 将 `find_indicators_split` 中早退之后的逻辑替换为：
   ```rust
   let start = len.saturating_sub(INDICATORS_WINDOW);
   let window = &data[start..];

   // HOT-02: 单次反向扫描 ':' 字节，检查关键字前缀，记录最左命中位置
   let mut search_end = window.len();
   let mut earliest = window.len();

   while search_end > 0 {
       match memchr::memrchr(b':', &window[..search_end]) {
           None => break,
           Some(colon) => {
               let prefix = &window[..colon];
               let kw_len = if prefix.ends_with(b"EXECTIME") {
                   Some(8usize)
               } else if prefix.ends_with(b"ROWCOUNT") {
                   Some(8usize)
               } else if prefix.ends_with(b"EXEC_ID") {
                   Some(7usize)
               } else {
                   None
               };
               if let Some(kw_len) = kw_len {
                   earliest = earliest.min(colon - kw_len);
               }
               search_end = colon; // 继续向左
           }
       }
   }

   let split = start + earliest;
   // CORR-03 验证守卫：假阳性时 fallback
   if split < len && parse_indicators_from_bytes(&data[split..]).is_none() {
       return len;
   }
   split
   ```

4. 同时删除 `use memchr::memmem::FinderRev;`（若其他地方未使用）。

确保函数总行数不超过 40 行（CLAUDE.md 规定）。若超出，将 "单次扫描" 逻辑提取为私有辅助函数 `scan_earliest_indicator(window: &[u8]) -> usize`。
  </action>
  <verify>
    <automated>cargo test 2>&1 | tail -10</automated>
  </verify>
  <done>
    - `cargo test` 全部通过
    - `src/sqllog.rs` 不再包含 `FINDER_REV_EXECTIME`、`FINDER_REV_ROWCOUNT`、`FINDER_REV_EXEC_ID`
    - `find_indicators_split` 包含 `memrchr` 调用和 `ends_with(b"EXECTIME")`
    - CORR-03 验证守卫 `parse_indicators_from_bytes.*is_none` 仍存在
    - `cargo clippy` 无 error
  </done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| 文件内容 → content_raw | mmap 映射的字节，来自外部文件，不可信 |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-03-01 | Tampering | find_indicators_split 早退逻辑 | accept | HOT-01 早退是额外过滤层，CORR-03 守卫仍然保留；假阳性（SQL 以 `.` 结尾）走守卫路径，无数据损坏风险 |
| T-03-02 | Tampering | HOT-02 memrchr 扫描越界 | accept | `slice::ends_with` 本身边界安全；`colon - kw_len` 在 `colon >= kw_len` 时正确（若 colon < kw_len，`ends_with` 已返回 false，不会进入减法分支）|
</threat_model>

<verification>
整体阶段验证（HOT-01/02 完成后）：

```bash
# 1. 全量测试
cargo test

# 2. Clippy 无 error
cargo clippy -- -D warnings

# 3. 确认静态变量已删除
grep -n "FINDER_REV_" src/sqllog.rs && echo "FAIL: 静态变量未删除" || echo "OK: 静态变量已清除"

# 4. 确认早退逻辑存在
grep -n "last_meaningful" src/sqllog.rs

# 5. 确认 memrchr 调用存在
grep -n "memrchr" src/sqllog.rs

# 6. 确认 CORR-03 守卫保留
grep -n "parse_indicators_from_bytes.*is_none" src/sqllog.rs
```
</verification>

<success_criteria>
- `cargo test` 退出码 0，全部测试通过
- `src/sqllog.rs` 包含 `last_meaningful != Some(b'.')` 早退逻辑
- `src/sqllog.rs` 包含 `memrchr(b':',` 调用
- `src/sqllog.rs` 不包含 `FINDER_REV_EXECTIME`、`FINDER_REV_ROWCOUNT`、`FINDER_REV_EXEC_ID`
- `src/sqllog.rs` 中 `find_indicators_split` 仍包含 `parse_indicators_from_bytes.*is_none` 守卫
- `cargo clippy -- -D warnings` 无 error
</success_criteria>

<output>
完成后创建 `.planning/phases/03-hotpath/03-01-SUMMARY.md`，记录：
- HOT-01/02 改动摘要
- 删除的静态变量列表
- 新增测试列表
- `cargo test` 输出摘要
</output>
