im_wower
·
2026-03-28
BUG-020-inmemory-deduper-unbounded.md
1# BUG-020: InMemory 去重器无上限,长期运行内存泄漏
2
3## 状态
4
5- 已修复(2026-03-28,代码已合入主线)
6
7## 当前代码结论
8
9- `InMemoryBaaInstructionDeduper` 已增加上限与淘汰
10- `InMemoryBaaLiveInstructionMessageDeduper` 已增加上限与淘汰
11- 当前策略保持简单 FIFO 式收口,目标是先避免常驻进程无限增长
12
13> 提交者:Claude(代码审查)
14> 日期:2026-03-27
15
16## 现象
17
18InMemoryBaaInstructionDeduper 和 InMemoryBaaLiveInstructionMessageDeduper 的 Set 只 add 不清理,没有 TTL 或 maxSize。长时间运行后 Set 无限增长。
19
20## 文件
21
22- `apps/conductor-daemon/src/instructions/dedupe.ts` InMemoryBaaInstructionDeduper
23- `apps/conductor-daemon/src/instructions/ingest.ts` InMemoryBaaLiveInstructionMessageDeduper
24
25## 修复
26
27加 maxSize 限制(默认 10000),超出时清理最旧的 20%。或改用 Map<string, number> 记录时间戳做 LRU:
28
29```typescript
30class InMemoryBaaInstructionDeduper {
31 private readonly keys = new Map<string, number>();
32 private readonly maxSize = 10000;
33
34 add(instruction) {
35 if (this.keys.size >= this.maxSize) {
36 const entries = [...this.keys.entries()].sort((a, b) => a[1] - b[1]);
37 for (let i = 0; i < entries.length * 0.2; i++) {
38 this.keys.delete(entries[i][0]);
39 }
40 }
41 this.keys.set(instruction.dedupeKey, Date.now());
42 }
43
44 has(dedupeKey) { return this.keys.has(dedupeKey); }
45}
46```
47
48## 严重度
49
50Low-Medium — 短期无影响,但 conductor 是常驻进程