[P1/高] BaseAdapter getFetchSignal 每次新建 timer 未清理,导致 timer 泄漏 #24

Open
opened 2026-07-21 21:55:52 +08:00 by thzxx · 0 comments
Owner

问题类型

缺陷 / 高 / LLM 适配器

文件位置

electron/harness/adapters/base-adapter.ts

问题描述

getFetchSignal 创建 AbortSignal + setTimeout 实现超时控制:

private getFetchSignal(timeoutMs: number): AbortSignal {
  const controller = new AbortController();
  setTimeout(() => controller.abort(), timeoutMs);
  return controller.signal;
}

问题:

  1. timer 未清理:请求成功完成后,setTimeout 仍在等待,造成 timer 句柄泄漏
  2. 多次请求累积:高频调用下 timer 数量线性增长
  3. Node.js 默认上限:Node 单进程 timer 上限约 10000,长时间运行后可能达到上限

影响

  • 内存与句柄泄漏
  • 长时间运行后性能下降

建议修复

private async fetchWithTimeout(url: string, init: RequestInit, timeoutMs: number) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);

  try {
    // 合并外部 signal 与超时 signal
    const externalSignal = init.signal;
    if (externalSignal) {
      if (externalSignal.aborted) controller.abort();
      else externalSignal.addEventListener('abort', () => controller.abort());
    }

    return await fetch(url, { ...init, signal: controller.signal });
  } finally {
    clearTimeout(timer); // 关键:清理 timer
  }
}

确保所有调用 getFetchSignal 的地方都最终清理 timer。

## 问题类型 缺陷 / 高 / LLM 适配器 ## 文件位置 `electron/harness/adapters/base-adapter.ts` ## 问题描述 getFetchSignal 创建 AbortSignal + setTimeout 实现超时控制: ```ts private getFetchSignal(timeoutMs: number): AbortSignal { const controller = new AbortController(); setTimeout(() => controller.abort(), timeoutMs); return controller.signal; } ``` 问题: 1. **timer 未清理**:请求成功完成后,setTimeout 仍在等待,造成 timer 句柄泄漏 2. **多次请求累积**:高频调用下 timer 数量线性增长 3. **Node.js 默认上限**:Node 单进程 timer 上限约 10000,长时间运行后可能达到上限 ## 影响 - 内存与句柄泄漏 - 长时间运行后性能下降 ## 建议修复 ```ts private async fetchWithTimeout(url: string, init: RequestInit, timeoutMs: number) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { // 合并外部 signal 与超时 signal const externalSignal = init.signal; if (externalSignal) { if (externalSignal.aborted) controller.abort(); else externalSignal.addEventListener('abort', () => controller.abort()); } return await fetch(url, { ...init, signal: controller.signal }); } finally { clearTimeout(timer); // 关键:清理 timer } } ``` 确保所有调用 getFetchSignal 的地方都最终清理 timer。
thzxx added the LLM?????? labels 2026-07-21 21:55:52 +08:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MetonaTeam/metona-ai-desktop#24