claude@macbookpro
·
2026-03-30
OPT-008-timed-jobs-async-log-writes.md
1# OPT-008: timed-jobs 日志改用异步写入避免阻塞事件循环
2
3> 提交者:Claude
4> 日期:2026-03-30
5
6## 现象
7
8`ConductorTimedJobs` 的 `writeLogEntry` 使用 `appendFileSync` 写 JSONL 日志。每个 tick 的 framework 日志 + 每个 runner 的启动/完成日志都会触发同步 IO。当 runner 数量增多或 tick 频率较高时,同步写入会阻塞事件循环,拖慢同一 tick 内后续 runner 的执行和其他并发请求的处理。
9
10## 当前行为
11
12```typescript
13// timed-jobs/runtime.ts writeLogEntry()
14appendFileSync(filePath, `${JSON.stringify(entry)}\n`);
15```
16
17每次 tick 至少写 2 条日志(tick_started + tick_completed),每个 runner 再加 2 条(runner_started + runner_completed)。当前 2 个 runner(projector + dispatcher),一个 tick 最少 6 次 `appendFileSync`。
18
19## 建议
20
21### 方案 A(推荐)
22
23改用 `appendFile`(异步版本),`writeLogEntry` 变为 fire-and-forget:
24
25```typescript
26appendFile(filePath, `${JSON.stringify(entry)}\n`, (err) => {
27 if (err) console.error(`[timed-jobs-log] write failed: ${String(err)}`);
28});
29```
30
31### 方案 B
32
33攒 buffer,每个 tick 结束后批量写一次:
34
35```typescript
36this.logBuffer.push(entry);
37// 在 tick 完成后
38appendFileSync(filePath, this.logBuffer.map(e => JSON.stringify(e)).join('\n') + '\n');
39this.logBuffer = [];
40```
41
42## 严重程度
43
44**Low**
45
46当前 2 个 runner + 10s interval,影响可忽略。但随 runner 增多或 interval 缩短会逐步放大。
47
48## 发现时间
49
50`2026-03-30 by Claude`
51
52## 相关代码
53
54- 同步写入:[apps/conductor-daemon/src/timed-jobs/runtime.ts](/Users/george/code/baa-conductor/apps/conductor-daemon/src/timed-jobs/runtime.ts) `writeLogEntry` 方法