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

Closed
opened 2026-07-21 21:55:52 +08:00 by thzxx · 1 comment
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
Author
Owner

修复说明

文件: electron/harness/adapters/base-adapter.ts + 4 个 Adapter

修复: 新增 fetchWithTimeout 方法(setTimeout + finally clearTimeout),确保请求完成后立即清理 timer。修改 deepseek/agnes/mimo/ollama 共 8 处 fetch + getFetchSignal 调用改用 fetchWithTimeout

验证: tsc --noEmit 类型检查通过。

## 修复说明 **文件**: `electron/harness/adapters/base-adapter.ts` + 4 个 Adapter **修复**: 新增 `fetchWithTimeout` 方法(`setTimeout` + `finally clearTimeout`),确保请求完成后立即清理 timer。修改 deepseek/agnes/mimo/ollama 共 8 处 `fetch + getFetchSignal` 调用改用 `fetchWithTimeout`。 **验证**: `tsc --noEmit` 类型检查通过。
thzxx closed this issue 2026-07-22 09:42:59 +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