From a45a4f1a5bb96f231c39e0e45af7741af5ff648c Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 1 Jul 2026 06:33:39 +0800 Subject: [PATCH] docs: record db-limited capacity findings --- README.md | 6 ++-- ...ackend-open-items-and-capacity-20260701.md | 13 +++++++- docs/refactor/next-development-todo.md | 4 +-- .../refactor/performance-benchmark-runbook.md | 2 +- .../performance-benchmark-summary-20260630.md | 21 +++++++++++-- ...docker-benchmark-resource-evidence-test.js | 2 ++ scripts/run-docker-4c16g-benchmark.js | 30 +++++++++++++++---- 7 files changed, 63 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 6f96acd0..64986bfd 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ | 对象存储/资料安全 | √ 可联调,待生产 AV/CDN | OSS/COS/Supabase Storage 签名、上传确认、短签名预览下载、水印 traceId、复检和安全扫描地基已完成;生产配置会拒绝 local_dev、非 HTTPS 公开 URL、非官方阿里云 OSS endpoint、阿里云 OSS 内网直签和未接外部扫描服务 | | PocketBase 真实数据迁移 | √ 本地跑通,待人工复核 blocker | SQLite 导出、标准化导入、校验和抽样脚本已跑通;正式切换前处理缺用户订单和缺归属手册章节 | | Taro H5 三端前端 | √ 第一版可构建 | 学生端、租户后台、平台后台均有真实 API 页面;已补 H5 `index.html` 模板和发布产物守卫;后续继续补小程序兼容、视觉精修、状态管理、包体优化和端到端测试 | -| 生产安全/压测交付 | △ 本地真实数据压测已跑,云端待复测 | 本地 Docker/Supabase 已完成真实迁移数据 30/50/100/150 并发只读和混合读写压测;最新 Docker API 受限资源复核为 30 并发只读 0 错误、349.7 req/s、P95 202ms,50 并发混合读写 0 错误、416.5 req/s、P95 215ms,100 并发混合读写 0 错误、394.2 req/s、P95 415ms,150 并发混合读写 0 错误、370.5 req/s、P95 622ms。50 worker 属于本地舒适区,100 可用但延迟偏高,150 是压力观察;上云后仍需执行生产 readiness、远程 Auth/RLS、4c16g 压测、PostgreSQL 调优、`security:repo`、真实 `@codex-security` 扫描和上线证据门禁。 | +| 生产安全/压测交付 | △ 本地真实数据压测已跑,云端待复测 | 本地 Docker/Supabase 已完成真实迁移数据 30/50/100/150 并发只读和混合读写压测;Docker API 受限资源复核为 30 并发只读 0 错误、349.7 req/s、P95 202ms,50 并发混合读写 0 错误、416.5 req/s、P95 215ms。进一步显式限制 DB 容器为 2 CPU/8G 后,30 只读和 50 混合读写均 0 错误但 P95 分别升至 1189ms/905ms,未通过上线延迟门禁。上云后必须执行生产 readiness、PostgreSQL shared-host 调优、远程 Auth/RLS、4C16G 复测、`security:repo`、真实 `@codex-security` 扫描和上线证据门禁。 | 更完整的进度看这些文档: @@ -640,6 +640,8 @@ README 只保留最新 Docker API 受限资源复核。更早的高资源 Docker | Docker API 2c4g / 50 | 60s | 10% | 25,157 | 0.00% | 416.54 req/s | 215.00 ms | 292.56 ms | | Docker API 2c4g / 100 | 60s | 8% | 23,873 | 0.00% | 394.18 req/s | 414.67 ms | 495.91 ms | | Docker API 2c4g / 150 | 60s | 6% | 22,523 | 0.00% | 370.53 req/s | 621.55 ms | 750.70 ms | +| Docker API 2c4g + DB 2c8g / 30 | 120s | 0% | 8,029 | 0.00% | 66.50 req/s | 1188.62 ms | 1697.71 ms | +| Docker API 2c4g + DB 2c8g / 50 | 60s | 10% | 7,088 | 0.00% | 115.82 req/s | 905.13 ms | 1211.98 ms | 只读上线门禁继续要求 `includeWrites=false`;混合读写报告需要显式使用 `--allow-writes` 做人工容量观察,例如: @@ -649,7 +651,7 @@ npm run perf:summary -- --input docs/refactor/performance-reports/api-benchmark- 使用 `--allow-writes` 时会输出 `capacityObservation`,不输出 `launchGateCheck`,不能把写入场景误填成生产上线门禁的只读证据。 -本地结论:当前 Docker Desktop 分配 20 CPU、约 62.7GB 内存,高于常见 4 核 16G 云服务器,不能直接作为生产 SLA。2026-07-01 05:57 受限 API 容器复核中,30 worker/120 秒只读上线门禁为 42,089 请求、0 错误、349.70 req/s、P95 202.20ms;50 worker/60 秒/10% 写入为 25,157 请求、0 错误、416.54 req/s、P95 215.00ms;100 worker/60 秒/8% 写入为 23,873 请求、0 错误、394.18 req/s、P95 414.67ms;150 worker/60 秒/6% 写入为 22,523 请求、0 错误、370.53 req/s、P95 621.55ms,属于压力区。按单学生 0.05 到 0.2 req/s 的页面节奏粗略折算,当前受限 API 容器舒适观察区间约对应 2,080 到 8,330 名活跃在线学生的请求吞吐;保守按生产首版 30% 到 50% 预留容量时,可先规划约 620 到 4,160 名活跃在线学生,等云端 4 核 16G 复测和真实前端埋点后再上调。正式对外容量承诺必须在目标 4 核 16G 云服务器、生产 PostgreSQL 参数、生产对象存储/CDN 和真实前端请求节奏下复跑。脱敏摘要和剩余功能清单见: +本地结论:当前 Docker Desktop 分配 20 CPU、约 62.7GB 内存,高于常见 4 核 16G 云服务器,不能直接作为生产 SLA。2026-07-01 05:57 只限制 API 容器时,50 worker 混合读写为 25,157 请求、0 错误、416.54 req/s、P95 215.00ms,这是“DB 未受限”的乐观观察;2026-07-01 06:26 同时限制 API 2C/4G 和 DB 2C/8G 时,30 worker 只读和 50 worker 混合读写仍 0 错误,但 P95 分别升至 1188.62ms 和 905.13ms,未通过上线延迟门禁,这是“DB 受限但未调 PostgreSQL 参数”的悲观观察。正式对外容量承诺必须在目标 4 核 16G 云服务器、生产 PostgreSQL shared-host 参数、`pg_stat_statements`、生产对象存储/CDN 和真实前端请求节奏下复跑。脱敏摘要和剩余功能清单见: ```text docs/refactor/performance-benchmark-summary-20260630.md diff --git a/docs/refactor/backend-open-items-and-capacity-20260701.md b/docs/refactor/backend-open-items-and-capacity-20260701.md index 27398d18..6140ada5 100644 --- a/docs/refactor/backend-open-items-and-capacity-20260701.md +++ b/docs/refactor/backend-open-items-and-capacity-20260701.md @@ -75,4 +75,15 @@ 当前本地结论:在受限 API 容器下,后端真实刷题读写链路的舒适观察区间约为 416 req/s,折算约 2,080 到 8,330 名活跃在线学生;100 worker 仍可用但 P95 已超过 400ms,150 worker 仍 0 错误但 P95 已超过 600ms,视为压力区,不建议作为生产承诺。受限 API 容器会明显压低上沿,因此上线承诺仍以云端受限资源复测为准。 -正式对外容量必须等 4 核 16G 云服务器部署后复跑。保守规划时,生产首版建议先按本地折算值的 30% 到 50% 做容量承诺,约 620 到 4,160 名活跃在线学生,等云端压测、CDN、对象存储和真实前端埋点完成后再上调。 +### 2026-07-01 Docker API + DB 显式受限复核 + +本轮使用同一压测入口,但显式设置 `BENCHMARK_LIMIT_DB_RESOURCES=true`,API 容器限制为 2 CPU/4G,Supabase PostgreSQL 容器限制为 2 CPU/8G,`DB_POOL_MAX=10`。PostgreSQL 参数仍是本地默认值,没有应用 `docs/refactor/postgresql-4c16g-tuning.md` 的 shared-host profile。 + +| 场景 | 并发 worker | 时长 | 刷题写入比例 | 请求数 | 错误率 | 吞吐 | P95 | P99 | 结论 | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | +| 只读上线门禁 | 30 | 120s | 0% | 8,029 | 0.00% | 66.50 req/s | 1188.62 ms | 1697.71 ms | 失败,延迟超过门禁 | +| 混合读写 | 50 | 60s | 10% | 7,088 | 0.00% | 115.82 req/s | 905.13 ms | 1211.98 ms | 失败,延迟超过观察线 | +| 混合读写 | 100 | 60s | 8% | 6,383 | 0.00% | 103.63 req/s | 1720.26 ms | 2101.02 ms | 压力区 | +| 混合读写 | 150 | 60s | 6% | 5,640 | 0.00% | 91.52 req/s | 2688.31 ms | 3284.99 ms | 压力区 | + +这组结果说明:代码在更严格资源下仍保持 0 错误,但默认 PostgreSQL 参数无法支撑上线延迟门禁。正式对外容量必须等 4 核 16G 云服务器部署、PostgreSQL shared-host 调参、`pg_stat_statements` 和慢 SQL 观察完成后复跑。没有云端复测前,不再把本地 05:57 API-only 观察值当生产容量承诺,只保留为乐观参考;06:26 DB 受限结果则作为未调参下限参考。 diff --git a/docs/refactor/next-development-todo.md b/docs/refactor/next-development-todo.md index 716f20ba..8bab7c77 100644 --- a/docs/refactor/next-development-todo.md +++ b/docs/refactor/next-development-todo.md @@ -95,7 +95,7 @@ - 已补 `npm run launch:gate` 生产上线证据门禁和 `docs/refactor/production-launch-evidence.template.json` 模板;最终切换前必须把 readiness、远程 Auth、RLS、PostgreSQL 4c16g 严格调参证据、生产 dry-run、导入校验、`pb:import:sample` 业务抽样、真实数据 API 只读压测、真实数据混合读写压测、API/worker/Taro、运行时审计、`security:repo`、真实 `@codex-security`、备份/回滚/真实抽样/生产 provider 等证据填入本地 `production-launch-evidence.json` 并通过门禁。当前 Codex 环境未暴露可调用的 `@codex-security` 扫描工具时,该项只能标为待补,不能伪造完成。 - 确认数据库迁移流程、备份恢复、日志、告警。 - 准备 API 容器部署和 Supabase 云端/自托管连接方案。 - - 已补 `npm run perf:api:local`、`npm run perf:summary`、`npm run perf:postgres:evidence`、`npm run perf:postgres:sql`、`npm run smoke:launch-persona`、`npm run smoke:taro:h5`、`npm run smoke:taro:h5:interaction`、`node scripts/taro-api-contract-test.js`、`node scripts/taro-persona-contract-test.js` 和 `docs/refactor/performance-benchmark-runbook.md`,可在本地或云端对真实迁移数据做只读门禁、混合读写容量观察、PostgreSQL 4c16g profile 调参证据、三类后端角色旅程烟测、三类 Taro 前端角色旅程契约、H5 发布目录启动烟测、真实浏览器关键点击烟测和前端 API 契约检查。2026-07-01 05:57 受限 API 容器真实迁移库复核中,30 worker/120s 只读为 42,089 请求、0 错误、349.70 req/s、P95 202.20ms、P99 295.16ms;50 worker/60s/10% 写入为 25,157 请求、0 错误、416.54 req/s、P95 215.00ms、P99 292.56ms;100 worker/60s/8% 写入为 23,873 请求、0 错误、394.18 req/s、P95 414.67ms、P99 495.91ms;150 worker/60s/6% 写入为 22,523 请求、0 错误、370.53 req/s、P95 621.55ms、P99 750.70ms。受限容器系列压测曾抓到自动勋章并发发放唯一键冲突,已修复并新增 `scripts/auto-badge-concurrency-test.js`。当前 Docker Desktop 给了 20 CPU/约 62.7GB 内存,但 API 容器限制为 2 CPU/4G,舒适观察区暂按 50 个无停顿 worker 估算,100 worker 可用但延迟偏高,150 worker 已是压力区;按单学生 0.05 到 0.2 req/s 粗略折算约为 2,080 到 8,330 名活跃在线学生的本机吞吐观察区间,正式容量仍以上云 4 核 16G 复测为准。 + - 已补 `npm run perf:api:local`、`npm run perf:summary`、`npm run perf:postgres:evidence`、`npm run perf:postgres:sql`、`npm run smoke:launch-persona`、`npm run smoke:taro:h5`、`npm run smoke:taro:h5:interaction`、`node scripts/taro-api-contract-test.js`、`node scripts/taro-persona-contract-test.js` 和 `docs/refactor/performance-benchmark-runbook.md`,可在本地或云端对真实迁移数据做只读门禁、混合读写容量观察、PostgreSQL 4c16g profile 调参证据、三类后端角色旅程烟测、三类 Taro 前端角色旅程契约、H5 发布目录启动烟测、真实浏览器关键点击烟测和前端 API 契约检查。2026-07-01 05:57 API-only 受限容器复核中,50 worker/60s/10% 写入为 25,157 请求、0 错误、416.54 req/s、P95 215.00ms、P99 292.56ms;2026-07-01 06:26 进一步显式限制 DB 容器为 2 CPU/8G 后,30 worker 只读为 8,029 请求、0 错误、66.50 req/s、P95 1188.62ms,50 worker/60s/10% 写入为 7,088 请求、0 错误、115.82 req/s、P95 905.13ms,均未通过上线延迟门禁。受限容器系列压测曾抓到自动勋章并发发放唯一键冲突,已修复并新增 `scripts/auto-badge-concurrency-test.js`。当前结论是:本地 API-only 结果只能作为乐观观察,DB 受限未调参结果作为悲观下限;正式容量仍以上云 4 核 16G、PostgreSQL shared-host 调参、真实前端埋点和生产对象存储/CDN 复测为准。 - 当前本地 PostgreSQL evidence 仍提示 `jit=on`、`statement_timeout=0`、`idle_in_transaction_session_timeout=0`、`lock_timeout=0`;上云后必须按 `docs/refactor/postgresql-4c16g-tuning.md` 调整参数,执行 `PG_TUNING_PROFILE=shared-host npm run perf:postgres:evidence -- --strict --json` 并通过后,再按 6/30/50/100 阶梯并发复跑容量报告并归档到本地上线证据。 - 本轮剩余功能和容量复核已经整理到 `docs/refactor/backend-open-items-and-capacity-20260701.md`。后续不要再把学生头像上传或默认排行榜当作待办;头像只保留男女预设,排行榜仅作为租户显式开启后的活动能力。 @@ -200,7 +200,7 @@ 4. 运维 - 后台操作审计报表。 - 定时备份、恢复演练。 - - 性能压测、慢 SQL、索引审查;当前已有 API 压测脚本、真实刷题读写闭环和 4 核 16G runbook。最新 Docker API 受限资源复核为 30 并发只读 0 错误、349.7 req/s、P95 202ms,50 并发混合读写 0 错误、416.5 req/s、P95 215ms,100 并发混合读写 0 错误、394.2 req/s、P95 415ms,150 并发混合读写 0 错误、370.5 req/s、P95 622ms。云服务器部署后还要按同一矩阵复跑并输出正式容量报告。 + - 性能压测、慢 SQL、索引审查;当前已有 API 压测脚本、真实刷题读写闭环和 4 核 16G runbook。API-only 受限结果可作为乐观观察;显式限制 DB 2C/8G 且未调 PostgreSQL 参数时,30 只读和 50 混合读写均 0 错误但未通过延迟门禁。云服务器部署后必须先按 PostgreSQL shared-host profile 调参,再按同一矩阵复跑并输出正式容量报告。 ## Taro 前端开发 TODO diff --git a/docs/refactor/performance-benchmark-runbook.md b/docs/refactor/performance-benchmark-runbook.md index 83a030a4..319552a5 100644 --- a/docs/refactor/performance-benchmark-runbook.md +++ b/docs/refactor/performance-benchmark-runbook.md @@ -188,7 +188,7 @@ Remove-Item Env:\DB_POOL_MAX 每次运行都会额外生成 `docker-4c16g-resource-evidence-*.json/md`,记录 Docker Desktop 总 CPU/内存、API 容器实际 CPU/内存限制、DB 容器是否被限制、`DB_POOL_MAX` 和每个压测报告的摘要。该文件位于已忽略的 `docs/refactor/performance-reports/`,用于防止后续把“只限制 API 容器”的本地结果误写成完整 4 核 16G 生产容量;它不能替代目标云服务器正式压测证据。 -如需要在本地显式限制 Supabase PostgreSQL 容器资源,可以打开下面开关。脚本默认会在结束后恢复 DB 容器原始限制;如果你希望保留限制用于排查,才设置 `BENCHMARK_KEEP_DB_LIMIT=true`。 +如需要在本地显式限制 Supabase PostgreSQL 容器资源,可以打开下面开关。脚本默认会在结束后恢复 DB 容器原始限制;如果原始容器是 unlimited,而当前 Docker Desktop 无法用 `docker update --memory 0` 清掉 live memory limit,脚本会恢复到 Docker Desktop 当前 CPU/内存上限附近并在资源证据中写明。只有你希望保留限制用于排查时,才设置 `BENCHMARK_KEEP_DB_LIMIT=true`。 ```powershell $env:BENCHMARK_LIMIT_DB_RESOURCES="true" diff --git a/docs/refactor/performance-benchmark-summary-20260630.md b/docs/refactor/performance-benchmark-summary-20260630.md index 6f060a28..80bc263c 100644 --- a/docs/refactor/performance-benchmark-summary-20260630.md +++ b/docs/refactor/performance-benchmark-summary-20260630.md @@ -134,11 +134,26 @@ 按 0.05 到 0.2 req/s/人的页面节奏粗略折算,50 worker 舒适吞吐约对应 2,080 到 8,330 名活跃在线学生请求吞吐;100 worker 上沿约对应 1,970 到 7,880 名;150 worker 压力区约对应 1,850 到 7,410 名。生产 4 核 16G 首版容量建议先按这组本地受限资源折算值的 30% 到 50% 保守规划,即约 620 到 4,160 名活跃在线学生请求吞吐,再以上云复测和真实前端埋点修正。 +### 2026-07-01 06:26 Docker API + DB 显式受限复核 + +本轮使用 `BENCHMARK_LIMIT_DB_RESOURCES=true npm run perf:api:docker-4c16g`,API 容器限制为 2 CPU/4G,Supabase PostgreSQL 容器限制为 2 CPU/8G,`DB_POOL_MAX=10`,压测鉴权继续使用 `PERF_AUTH_MODE=app_session`。这比 05:57 只限制 API 容器更接近本地 4C16G 资源压力,但 PostgreSQL 参数仍是本地默认值,并未按 `docs/refactor/postgresql-4c16g-tuning.md` 应用 shared-host profile。 + +| 场景 | 并发 worker | 时长 | 刷题写入比例 | 请求数 | 错误率 | 吞吐 | P95 | P99 | 门禁结论 | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | +| 只读上线门禁 | 30 | 120s | 0% | 8,029 | 0.00% | 66.50 req/s | 1188.62 ms | 1697.71 ms | 失败,超过 P95/P99 | +| 混合读写 | 50 | 60s | 10% | 7,088 | 0.00% | 115.82 req/s | 905.13 ms | 1211.98 ms | 失败,超过 P95/P99 | +| 混合读写 | 100 | 60s | 8% | 6,383 | 0.00% | 103.63 req/s | 1720.26 ms | 2101.02 ms | 压力区 | +| 混合读写 | 150 | 60s | 6% | 5,640 | 0.00% | 91.52 req/s | 2688.31 ms | 3284.99 ms | 压力区 | + +结论:资源更严格后系统仍保持 0 错误,但延迟不满足上线门禁。该结果不能作为生产容量承诺,反而证明云端正式部署前必须先完成 PostgreSQL shared-host 调参、`pg_stat_statements`、慢 SQL 观察和 4C16G 云服务器复测。尤其是 `learning.trend`、`learning.stats`、`learning.practice_flow.create/detail/answer` 在受限 DB 下最容易抬高 P95,应在云端结合慢 SQL 和 `EXPLAIN (ANALYZE, BUFFERS)` 继续看是否需要预聚合、缓存或索引微调。 + +按 50 worker 混合读写 115.82 req/s 粗略折算,理论请求吞吐约对应 580 到 2,316 名活跃在线学生,但因为该轮未完成 PostgreSQL 调参且只在本地 Docker Desktop 上运行,只能作为“未调参受限资源下限观察”,不能作为首版生产规划值。首版生产规划仍以上云调参后复测为准。 + ## 初步结论 -- 本地高资源 Docker Desktop 历史结果显示代码和索引在 50/100 worker 下有较高吞吐;但最新受限 API 容器复核更接近保守上线口径,50 worker 混合读写 P95 约 215ms,100 worker P95 约 415ms,150 worker P95 约 622ms 已经进入压力区。 -- 按最新 416.54 req/s 的 50 worker 舒适吞吐粗略折算,如果未来前端真实埋点显示每名在线学生平均 0.05 到 0.2 req/s,则理论请求吞吐约对应 2,080 到 8,330 名活跃在线学生;按 100 worker 上沿 394.18 req/s 估算约为 1,970 到 7,880 名;按 150 worker 压力区 370.53 req/s 估算约为 1,850 到 7,410 名。这只是吞吐换算,不是生产 SLA。 -- 生产如果使用 4 核 16G 单机,首版容量承诺建议先按最新本地受限资源折算值的 30% 到 50% 保守规划,即约 620 到 4,160 名活跃在线学生请求吞吐,再以云端复测、真实前端埋点、CDN/对象存储和生产 PostgreSQL 调参后结果上调。 +- 本地高资源 Docker Desktop 历史结果显示代码和索引在 50/100 worker 下有较高吞吐;只限制 API 容器时,50 worker 混合读写 P95 约 215ms,100 worker P95 约 415ms,150 worker P95 约 622ms;进一步把 DB 容器也限制为 2 CPU/8G 后,30 worker 只读和 50 worker 混合读写均未通过延迟门禁。 +- 最新 DB 显式受限结果说明:不能仅凭本地 Docker API 受限跑分给生产承诺,生产 4C16G 必须先应用 PostgreSQL shared-host 调参并启用 `pg_stat_statements`,再跑正式只读/混合读写矩阵。 +- 生产如果使用 4 核 16G 单机,首版容量承诺不要再直接沿用 05:57 的 620 到 4,160 人估算;应先以云端调参后复测结果为准。没有云端复测前,可以把 05:57 结果看作“API 受限但 DB 未受限的乐观观察”,把 06:26 结果看作“DB 也受限但未调参的悲观观察”。 - 正式容量承诺必须在目标 4 核 16G 云服务器、生产 PostgreSQL 参数、对象存储/CDN、真实前端请求节奏和生产网络下复跑。 ## 后续复测 diff --git a/scripts/docker-benchmark-resource-evidence-test.js b/scripts/docker-benchmark-resource-evidence-test.js index 11a15f55..219f2c98 100644 --- a/scripts/docker-benchmark-resource-evidence-test.js +++ b/scripts/docker-benchmark-resource-evidence-test.js @@ -17,7 +17,9 @@ assert.match(script, /BENCHMARK_KEEP_DB_LIMIT/, 'benchmark script should support assert.match(script, /dockerJson\(\['info', '--format', '\{\{json \.\}\}'\]\)/, 'benchmark script should record Docker Desktop resources'); assert.match(script, /inspectContainer\(composeServiceContainerId\('api'\)\)/, 'benchmark script should inspect the API container after compose starts'); assert.match(script, /inspectContainer\(dbContainerName\)/, 'benchmark script should inspect the Supabase DB container'); +assert.match(script, /dockerHostRestoreLimit/, 'benchmark script should restore originally-unlimited DB containers to Docker host limits'); assert.match(script, /restoreDbLimit\(dbRestore, resourceEvidence\)/, 'benchmark script should restore DB resource limits by default'); +assert.match(script, /restoreLimit = restore/, 'benchmark script should record the DB restore strategy in evidence'); assert.match(script, /readBenchmarkSummary\(report\)/, 'benchmark script should attach benchmark summaries to resource evidence'); assert.match(runbook, /docker-4c16g-resource-evidence-\*\.json\/md/, 'runbook should document the resource evidence artifact'); diff --git a/scripts/run-docker-4c16g-benchmark.js b/scripts/run-docker-4c16g-benchmark.js index 6fafb2a5..0d1a9702 100644 --- a/scripts/run-docker-4c16g-benchmark.js +++ b/scripts/run-docker-4c16g-benchmark.js @@ -48,7 +48,6 @@ function runCapture(command, args, options = {}) { return spawnSync(command, args, { cwd: repoRoot, encoding: 'utf8', - shell: process.platform === 'win32', env: baseEnv(options.env), }); } @@ -118,6 +117,20 @@ function dockerInfoSummary() { }; } +function dockerHostRestoreLimit() { + const info = dockerInfoSummary(); + const ncpu = Number(info.ncpu || 0); + const memoryBytes = Number(info.memoryBytes || 0); + return { + cpus: ncpu > 0 ? String(ncpu) : '0', + memory: memoryBytes > 0 ? String(Math.floor(memoryBytes * 0.98)) : '0', + memorySwap: memoryBytes > 0 ? String(Math.floor(memoryBytes * 0.98)) : '-1', + note: ncpu > 0 && memoryBytes > 0 + ? 'Original DB container was unlimited; Docker Desktop may not clear live memory limits with docker update, so restore uses the Docker Desktop host limit.' + : 'Original DB container was unlimited but Docker host resources were unavailable; attempted docker update unlimited restore.', + }; +} + function resourceSummaryFromHostConfig(hostConfig = {}) { const nanoCpus = Number(hostConfig.NanoCpus || 0); const memoryBytes = Number(hostConfig.Memory || 0); @@ -191,11 +204,14 @@ function maybeApplyDbLimit(resourceEvidence) { } const beforeRaw = resourceEvidence.database.before.resources.raw; - const restore = { - cpus: beforeRaw.nanoCpus > 0 ? String(beforeRaw.nanoCpus / 1_000_000_000) : '0', - memory: String(beforeRaw.memory || 0), - memorySwap: String(beforeRaw.memorySwap || 0), - }; + const restore = beforeRaw.nanoCpus > 0 || beforeRaw.memory > 0 + ? { + cpus: beforeRaw.nanoCpus > 0 ? String(beforeRaw.nanoCpus / 1_000_000_000) : '0', + memory: String(beforeRaw.memory || 0), + memorySwap: String(beforeRaw.memorySwap || 0), + note: 'Restoring DB container resource limits captured before benchmark.', + } + : dockerHostRestoreLimit(); try { updateContainerResources(dbContainerName, { @@ -219,6 +235,7 @@ function restoreDbLimit(restore, resourceEvidence) { try { updateContainerResources(dbContainerName, restore); resourceEvidence.database.restored = true; + resourceEvidence.database.restoreLimit = restore; resourceEvidence.database.afterRestore = inspectContainer(dbContainerName); } catch (error) { resourceEvidence.database.restored = false; @@ -381,6 +398,7 @@ function resourceEvidenceMarkdown(evidence) { if (evidence.database?.afterRestore) { lines.push(`- DB 恢复后:${evidence.database.afterRestore.resources.cpus ?? 'unlimited'} CPU / ${evidence.database.afterRestore.resources.memory}`); } + if (evidence.database?.restoreLimit?.note) lines.push(`- DB 恢复策略:${evidence.database.restoreLimit.note}`); if (evidence.database?.note) lines.push(`- 说明:${evidence.database.note}`); if (evidence.database?.limitError) lines.push(`- DB 限制错误:${evidence.database.limitError}`); if (evidence.database?.restoreError) lines.push(`- DB 恢复错误:${evidence.database.restoreError}`);