diff --git a/docs/PERFORMANCE_AND_OPEN_ISSUE_TRIAGE_2026-08-16.md b/docs/PERFORMANCE_AND_OPEN_ISSUE_TRIAGE_2026-08-16.md index 8df6a3bf..d73e16e8 100644 --- a/docs/PERFORMANCE_AND_OPEN_ISSUE_TRIAGE_2026-08-16.md +++ b/docs/PERFORMANCE_AND_OPEN_ISSUE_TRIAGE_2026-08-16.md @@ -68,6 +68,37 @@ - P0-4 已完成:系统设置提供运行实例/任务统计保留天数,默认 0(禁用);清理前展示记录数与依赖缓存字节数,清理接口要求明确确认。依赖缓存和 SQLite `VACUUM` 均为手动勾选,不在启动或升级时自动执行。 - P0-5 已完成:Debian 镜像构建后清理 npm cache 和 apt lists。 +## 优化后实测(提交 `276dfc23`,同机 Node 24 / Docker Desktop arm64) + +### 指标内存 + +| 压力 | 基线 | 优化后 | 结果 | +| --- | ---: | ---: | --- | +| 50,000 条保留样本 | 50,000 | 1,000 | 减少 98% | +| 50,000 条 GC 后堆增量 | 8.76 MiB | 0.16 MiB | 减少约 98.2% | +| 500,000 条 GC 后堆增量 | 88.33 MiB | 0.11 MiB | 减少约 99.9% | +| 500,000 条写入耗时(5 轮平均) | 149.6 ms | 74.4 ms | 约快 50% | +| 500,000 条统计查询 | `RangeError` 栈溢出 | 正常 | 消除故障 | + +真实容器用并发 50 发出 50,000 次 `/api/health`:两者空闲均约 161 MiB;基线负载后即时增加约 108.5 MiB、稳定后仍增加约 17.6 MiB,优化版分别约 81.0 MiB 和 8.4 MiB,改善约 25% 和 52%。三轮 10,000 请求耗时基线平均 3.86 秒、优化版 4.05 秒,差约 4.8% 且落在轮次波动内,因此不宣称 HTTP 吞吐提升。 + +### 大日志读取 + +对 100 MiB 日志做三轮读取: + +| 指标 | 基线整文件读取 | 优化后 tail 读取 | 结果 | +| --- | ---: | ---: | --- | +| 返回数据 | 100 MiB | 256 KiB | 减少 99.75%(400 倍) | +| 平均耗时 | 56.1 ms | 1.34 ms | 约快 41.9 倍 | +| 堆峰值增量 | 100.11 MiB | 0.317 MiB | 减少约 99.7% | +| RSS 峰值增量 | 115.18 MiB | 0.349 MiB | 减少约 99.7% | + +### 日志写入、SQLite 与镜像 + +- PM2 实际容器测试中,基线为约 7,288 行输出额外生成了 888,026 bytes 的 `qinglong-out.log`;优化后 PM2 日志目录没有文件,Docker stdout 每行只出现一次。测试同时发现 `/proc/1/fd/1` 会造成 Docker stdout 重复,最终改为容器中 `out_file/error_file=/dev/null`,由 `pm2-runtime` 单独转发 stdout。 +- 合成 100,000 条运行实例和 100,000 条任务统计的 SQLite 数据库为 24,481,792 bytes。删除 90% 历史记录耗时 0.26 秒,文件大小不会立刻变化;可选 `VACUUM` 耗时 0.05 秒,文件降至 2,465,792 bytes,回收约 89.9%。 +- 当前 Debian 镜像 `/var/lib/apt/lists` 实测 19,527,172 bytes(18.62 MiB),新 Dockerfile 会在同一个安装层删除。Dockerfile `--check` 无警告;完整镜像构建在 Debian 包升级网络等待阶段停止,因此不虚报最终镜像总大小。 + ### P1:轻量架构调整 1. 主进程只加载 cluster/bootstrap 必需模块,把 Express、监控、HTTP 中间件和业务容器全部延迟到 HTTP worker。目标是先回收主进程约 30–40 MiB private memory。 diff --git a/ecosystem.config.js b/ecosystem.config.js index 95e702bf..d7c150e2 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -10,8 +10,8 @@ module.exports = { listen_timeout: 5000, source_map_support: true, time: !isContainer, - out_file: isContainer ? '/proc/1/fd/1' : undefined, - error_file: isContainer ? '/proc/1/fd/2' : undefined, + out_file: isContainer ? '/dev/null' : undefined, + error_file: isContainer ? '/dev/null' : undefined, script: 'static/build/app.js', env: { http_proxy: '', diff --git a/test/back/ecosystem.test.cjs b/test/back/ecosystem.test.cjs index 6fb7512c..34de98ab 100644 --- a/test/back/ecosystem.test.cjs +++ b/test/back/ecosystem.test.cjs @@ -13,10 +13,10 @@ function loadConfig(containerValue) { return require(configPath).apps[0]; } -test('container logging goes to the container standard streams', () => { +test('container logging relies on pm2-runtime stdout without persistent copies', () => { const app = loadConfig('true'); - assert.equal(app.out_file, '/proc/1/fd/1'); - assert.equal(app.error_file, '/proc/1/fd/2'); + assert.equal(app.out_file, '/dev/null'); + assert.equal(app.error_file, '/dev/null'); assert.equal(app.time, false); });