Files
openless/.github/TEST_VERIFICATION.md
T

7.3 KiB
Raw Blame History

Issue #238 修复验证报告

修复内容总结

本次修复解决了"录音器异常停止后触发 ASR 超时,导致胶囊无响应"的问题,包含以下 4 个关键修复:

  1. Recorder Liveness Watchdog - 检测录音回调静默停止
  2. Coordinator 全局超时保护 - 15秒兜底超时,确保胶囊状态恢复
  3. ASR 资源清理 - 超时时调用 asr.cancel() 清理 WebSocket
  4. Watchdog 计时优化 - 从 stream.play() 后开始计时,避免慢启动误报

代码审查验证

1. Recorder Watchdog 逻辑验证

文件: openless-all/app/src-tauri/src/recorder.rs

关键代码位置: 第 144-183 行

验证点:

✅ Watchdog 线程启动时机: 在 stream.play() 成功后启动(line 144)

  • 确保只有在音频流真正开始后才开始监控
  • 避免将设备初始化时间计入超时预算

✅ 计时起点正确: 使用 watchdog_start_time = Instant::now()(line 147)

  • 不依赖 StreamState::stream_start_time
  • 从 watchdog 真正开始监控时计时

✅ 双模式检测:

  • None 分支(line 166-179): 检测"首次回调永远不到达"

    • 使用 watchdog_start_time.elapsed()
    • 超时阈值: 5 秒
    • 错误消息: "录音启动后 5 秒内未收到回调"
  • Some 分支(line 152-164): 检测"回调中途停止"

    • 使用 last_time.elapsed()
    • 超时阈值: 3 秒
    • 错误消息: "录音回调静默停止 X 秒"

✅ 时间戳更新: process_callback 在成功调用 consumer 后更新(recorder.rs:389)

  • 只有在真正处理音频数据后才更新时间戳
  • 避免空数据导致的误判

✅ 错误通知: 通过 runtime_error_tx 发送 RecorderError::EngineFailed

  • 错误会传播到 coordinator
  • 触发胶囊状态恢复

2. Coordinator 全局超时验证

文件: openless-all/app/src-tauri/src/coordinator.rs

关键代码位置:

  • Dictation 路径: 第 1367-1403 行
  • QA 路径: 第 2288-2310 行

验证点:

✅ 超时时间设置: COORDINATOR_GLOBAL_TIMEOUT_SECS = 15(line 30)

  • 比 ASR 的 12 秒超时稍长
  • 作为最后的防线,只在 ASR 超时机制失效时触发

✅ Dictation 路径超时保护(line 1368-1403):

  • 使用 tokio::time::timeout 包装 await_final_result()
  • 成功路径: Ok(Ok(r)) - 正常返回结果
  • ASR 错误路径: Ok(Err(e)) - ASR 报告错误,恢复状态
  • 全局超时路径: Err(_) - 15秒超时,强制恢复

✅ QA 路径超时保护(line 2288-2310):

  • 相同的超时逻辑
  • 使用 finish_qa_with_error 恢复 QA 状态

✅ 超时时的资源清理:

  • 关键: 调用 asr.cancel()(line 1393, 2304)
  • 清理 WebSocket 连接和 worker 线程
  • 防止资源泄漏

✅ 状态恢复完整性:

  • 发送 Error 胶囊事件
  • 恢复 Windows IME session
  • 设置 phase 为 Idle
  • 调度胶囊自动隐藏

3. 错误传播路径验证

完整的错误传播链:

Recorder 回调停止
  ↓
Watchdog 检测到(3秒或5秒)
  ↓
发送 RecorderError::EngineFailed 到 runtime_error_tx
  ↓
Coordinator 的 recorder_error_rx 接收
  ↓
调用 handle_recorder_error()
  ↓
取消 ASR session
  ↓
恢复胶囊状态到 Idle

验证: 检查 coordinator.rs 中的错误监听实现

✅ Dictation 路径错误监听(line 1146-1148):

  • Recorder 启动时返回 runtime_errors channel
  • 调用 spawn_recorder_error_monitor 启动监听线程

✅ QA 路径错误监听(line 2212-2216):

  • QA 录音同样启动 spawn_qa_recorder_error_monitor
  • 使用独立的 session_id 守卫

✅ 错误监听器实现(line 1173-1197):

  • 捕获 session_id,防止处理过期事件
  • 接收到错误后调用 abort_recording_with_error
  • 日志记录: "[coord] recorder runtime error: {err}"

✅ 错误中止实现(line 1226-1250):

  • 调用 begin_recording_abort_before_restore 获取中止上下文
  • 清理启动资源: discard_startup_resources_for_session
  • 恢复 Windows IME session
  • 发送 Error 胶囊事件
  • 恢复状态到 Idle

4. 边界情况分析

4.1 慢启动设备

场景: 设备初始化需要 2 秒,stream.play() 需要 1 秒

预期行为:

  • ✅ Watchdog 从 stream.play() 成功后开始计时
  • ✅ 5 秒预算完全用于等待首次回调
  • ✅ 不会因为设备慢而误报

验证: watchdog_start_time 在 watchdog 线程内部初始化(line 147)

4.2 长时间静音

场景: 用户触发录音但不说话,保持 10 秒

预期行为:

  • ✅ 回调持续执行(即使是静音数据)
  • ✅ last_callback_time 持续更新
  • ✅ 不触发 watchdog 超时
  • ✅ 正常完成识别流程

验证: process_callback 在处理任何非空数据后都会更新时间戳

4.3 网络中断

场景: ASR WebSocket 连接失败或中断

预期行为:

  • ✅ ASR 层报告错误或超时(12秒)
  • ✅ 如果 ASR 超时机制失效,全局超时在 15 秒触发
  • ✅ 调用 asr.cancel() 清理资源
  • ✅ 胶囊恢复到 Idle

验证: 全局超时的 Err(_) 分支包含 asr.cancel() 调用

4.4 快速开关

场景: 快速启动/停止录音 5 次

预期行为:

  • ✅ 每次停止时 stop_flag 设置为 true
  • ✅ Watchdog 线程检测到 stop_flag 并退出
  • ✅ 主线程等待 watchdog 退出(line 194-196)
  • ✅ 不会有多个 watchdog 线程同时运行

验证: run_audio_thread 在退出前等待 watchdog(line 194-196)

潜在风险评估

低风险

  1. 正常流程不受影响: 所有修改都是防御性的,不改变正常路径
  2. 超时阈值保守: 5秒/3秒/15秒都足够宽松,不会误报
  3. 资源清理完整: 超时时正确调用 asr.cancel()

需要运行时验证的场景

以下场景需要在真实环境中测试,无法通过代码审查完全验证:

  1. CPAL 回调真的会静默停止吗?

    • 需要在 Windows 上复现 Issue #238 的场景
    • 验证 watchdog 能否检测到
  2. Watchdog 线程的性能影响

    • 每秒检查一次,理论上开销很小
    • 需要在低端设备上验证
  3. 多次超时恢复的稳定性

    • 连续触发 10 次超时,观察是否有资源泄漏
    • 验证状态机是否始终能恢复

代码质量评估

优点

✅ 防御深度: 三层防护(Recorder watchdog → ASR timeout → Coordinator global timeout) ✅ 错误传播清晰: 通过 channel 传递错误,不依赖共享状态 ✅ 资源清理完整: 超时时调用 asr.cancel() ✅ 日志完善: 每个关键路径都有日志输出 ✅ 计时准确: Watchdog 从正确的时间点开始计时

改进建议

💡 可选: 添加 metrics 统计

  • 记录 watchdog 触发次数
  • 记录全局超时触发次数
  • 帮助监控线上问题

💡 可选: 可配置的超时阈值

  • 允许用户在设置中调整超时时间
  • 适应不同性能的设备

结论

代码审查结果: ✅ 通过

所有关键逻辑都已正确实现:

  1. ✅ Watchdog 从正确的时间点开始计时
  2. ✅ 双模式检测覆盖所有故障场景
  3. ✅ 全局超时作为最后防线
  4. ✅ 资源清理完整,无泄漏风险
  5. ✅ 错误传播路径清晰
  6. ✅ 边界情况处理正确

建议:

  • 可以直接向上游提交 PR
  • 在 PR 描述中说明需要在 Windows 上测试验证
  • 如果维护者反馈有问题,再根据实际情况调整