Skip to content

[Performance] Creating a new session hangs/freezes due to multiple full session-directory scans #4385

@Lumoa-dev

Description

@Lumoa-dev

描述 (Description)

在桌面端点击"新建会话"("New Session" 按钮),UI 会卡住 1-3 秒才出现空白会话。创建新会话这种轻量操作本应是瞬间完成的。

Clicking "New Session" in the desktop app causes noticeable UI freezing for 1-3 seconds before the blank session appears. This should be nearly instantaneous.


触发条件 (When it happens)

  • 只要有多个已保存会话(5+ 个)就会有明显卡顿
  • 保存的会话越多,卡顿越严重
  • 每次创建新标签页/新会话都会触发(包括应用启动时的 buildTabController
  • Windows 上更明显(文件 I/O 较慢),macOS/Linux 也有

根因分析 (Root Cause)

问题代码位置

desktop/tabs.go:921-1079buildTabController 方法

创建新会话时,串行执行了多次全量会话文件扫描

migrateLegacySessionsIntoGlobalTopics × 2 次

// Line 976 — 第一次调用,全局扫描
migratedGlobalTopics := migrateLegacySessionsIntoGlobalTopics(config.SessionDir())
...

// Line 1024 — 第二次调用,项目扫描
migratedTopics := migrateLegacySessionsIntoGlobalTopics(dir)

migrateLegacySessionsIntoGlobalTopics 内部调用 agent.ListSessions(dir)internal/agent/save.go:101),它会:

  1. os.ReadDir(dir) — 读取目录所有条目
  2. 遍历每个 .jsonl 文件 → previewSession(full) — 打开文件、JSON 解码、统计用户消息数
  3. 再读 .meta 侧车文件

这意味着每次调用都要解码磁盘上每一个会话文件

findTopicSession — 再扫一次

// Line 1050 — 第三次扫描
existingPath := findTopicSession(dir, tab.TopicID)

findTopicSessiondesktop/tabs.go:3312)又做了一次 os.ReadDir + 遍历所有 .jsonl → 读 .meta 文件 → 比较 TopicID。

boot.Build — 额外开销

// Line 999 — 组装 Controller
ctrl, err := boot.Build(buildCtx, boot.Options{...})

boot.Build 内部会初始化 MCP 插件、工具注册表、代码检查、事件系统等,也涉及 I/O。

汇总

一个简单的"新会话"操作触发了:

步骤 操作 I/O 类型
migrateLegacySessionsIntoGlobalTopics (全局) 扫描全部 .jsonl + 解码 全量磁盘读
migrateLegacySessionsIntoGlobalTopics (项目) 再扫一次全部 .jsonl + 解码 全量磁盘读
boot.Build 配置加载 + MCP 发现 配置读
findTopicSession 第三次扫 .jsonl + 读 .meta 全量磁盘读

三次全量扫描都是 O(n) 的完整文件 I/O,且全部在 UI 线程路径上。


修复建议 (Proposed Fix)

方案 A(推荐):给 agent.ListSessions 加内存缓存。缓存的 key 是目录路径 + 文件 mtime 列表,当文件列表或 mtime 无变化时直接返回缓存的 []SessionInfo,避免重复解码。

方案 B:把 migrateLegacySessionsIntoGlobalTopics 改为增量迁移——只在首次运行时执行全量扫描,之后只检查新增文件。当前每次 buildTabController 都跑全量扫描是不必要的。

方案 C:把 findTopicSession 的结果也纳入缓存,或者将 TopicID 索引写入一个独立的索引文件(JSON Map),避免遍历所有文件。


环境 (Environment)

  • Reasonix v2 (Go rewrite, main-v2 branch)
  • 桌面端 (Wails desktop app)
  • Windows 11(复现最明显),macOS / Linux 也存在

相关代码文件

  • desktop/tabs.gobuildTabController(第 921-1079 行)
  • desktop/tabs.gomigrateLegacySessionsIntoGlobalTopics(第 2117 行)
  • desktop/tabs.gofindTopicSession(第 3312 行)
  • internal/agent/save.goListSessions(第 101 行)

补充

已经有 PR #4337 修复了 Windows 上会话切换卡顿的问题,使用了 RLock 替代 Lock 等优化。但此处是全量扫描的问题,与锁类型无关,需要不同的修复策略。

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentCore agent loop (internal/agent, internal/control)desktopWails desktop app (desktop/**)windowsWindows-specific

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions