From 49358aae6b710b255edd64f8078641cda6768f36 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 1 Jul 2026 03:01:02 +0800 Subject: [PATCH] docs: record backend capacity review --- README.md | 12 +++- apps/api/src/features/tenant-admin/routes.ts | 10 ++- docs/refactor/README.md | 22 +++--- ...ackend-open-items-and-capacity-20260701.md | 70 +++++++++++++++++++ docs/refactor/next-development-todo.md | 3 +- .../performance-benchmark-summary-20260630.md | 25 +++++-- scripts/api-integration-test.js | 2 +- .../import-pocketbase/src/validate-import.ts | 2 + 8 files changed, 124 insertions(+), 22 deletions(-) create mode 100644 docs/refactor/backend-open-items-and-capacity-20260701.md diff --git a/README.md b/README.md index 4d19823c..d37b4243 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## 当前状态 -更新时间:2026-06-30 +更新时间:2026-07-01 目前已经完成并在本地验证通过的内容: @@ -75,6 +75,7 @@ - `docs/refactor/blueprint-coverage.md` - `docs/refactor/api-structure.md` - `docs/refactor/web-launch-acceptance-checklist.md` +- `docs/refactor/backend-open-items-and-capacity-20260701.md` ## 目录结构 @@ -593,7 +594,7 @@ npm --silent run smoke:taro:h5:interaction -- --json > docs/refactor/launch-arti node scripts\taro-h5-release-guardrails-test.js --require-dist --require-runtime-config --json > docs/refactor/launch-artifacts/taro-h5-release-guardrails.json ``` -最近一次本地真实迁移库数据规模约为 74,117 题、1,601 个题目合集、3,106 个练习蓝图、3,505 个单词、2,678 条知识手册、3,690 个用户、113,810 条答题记录、38,207 条错题和 458 条权益。压测 worker 是无停顿请求流,不能直接等同于真实在线学生数;前端完成后需要用真实页面埋点估算单个学生平均 RPS,再折算在线容量。 +最近一次本地真实迁移库已包含前期压测写入记录,当前规模约为 74,117 题、1,601 个题目合集、3,106 个练习蓝图、3,505 个单词、2,678 条知识手册、3,690 个用户、196,846 条答题记录、38,207 条错题和 466 条权益。压测 worker 是无停顿请求流,不能直接等同于真实在线学生数;前端完成后需要用真实页面埋点估算单个学生平均 RPS,再折算在线容量。 | 并发 worker | 时长 | 刷题写入比例 | 请求数 | 错误率 | 吞吐 | P95 | P99 | | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | @@ -612,6 +613,10 @@ node scripts\taro-h5-release-guardrails-test.js --require-dist --require-runtime | 150 | 120s | 6% | 82,693 | 0.00% | 682.40 req/s | 369.90 ms | 493.69 ms | | 100 | 120s | 8% | 84,694 | 0.00% | 699.54 req/s | 265.11 ms | 337.82 ms | | 150 | 120s | 6% | 74,209 | 0.00% | 612.19 req/s | 446.37 ms | 579.39 ms | +| 30 | 120s | 0% | 64,382 | 0.00% | 532.97 req/s | 140.64 ms | 193.69 ms | +| 50 | 60s | 10% | 40,065 | 0.00% | 658.11 req/s | 151.63 ms | 188.07 ms | +| 100 | 60s | 8% | 37,290 | 0.00% | 610.57 req/s | 303.93 ms | 376.47 ms | +| 150 | 60s | 6% | 33,952 | 0.00% | 555.08 req/s | 490.53 ms | 639.05 ms | 只读上线门禁继续要求 `includeWrites=false`;混合读写报告需要显式使用 `--allow-writes` 做人工容量观察,例如: @@ -621,10 +626,11 @@ npm run perf:summary -- --input docs/refactor/performance-reports/api-benchmark- 使用 `--allow-writes` 时会输出 `capacityObservation`,不输出 `launchGateCheck`,不能把写入场景误填成生产上线门禁的只读证据。 -本地结论:100 个无停顿 worker、120 秒、8% 刷题写入闭环复测为 84,694 请求、0 错误、699.54 req/s、P95 265.11ms、P99 337.82ms,仍可作为当前本机 Docker 舒适区;150 个无停顿 worker、120 秒、6% 写入为 74,209 请求、0 错误、612.19 req/s、P95 446.37ms、P99 579.39ms,已进入压力区并出现吞吐回落。按单学生 0.05 到 0.2 req/s 的页面节奏粗略折算,100 worker 舒适区约对应 3,500 到 14,000 名活跃在线学生的请求吞吐观察区间;150 worker 压力区约对应 3,000 到 12,200 名。这个折算不是生产 SLA,压测 worker 也不等于在线人数。当前 PostgreSQL evidence 提醒本地默认库仍是 `jit=on` 且 `statement_timeout`、`idle_in_transaction_session_timeout`、`lock_timeout` 未设置,上云必须按调参文档复核。正式对外容量承诺必须在目标 4 核 16G 云服务器、生产 PostgreSQL 参数、生产对象存储/CDN 和真实前端请求节奏下复跑。脱敏摘要见: +本地结论:当前 Docker Desktop 分配 20 CPU、约 62.7GB 内存,高于常见 4 核 16G 云服务器,不能直接作为生产 SLA。2026-07-01 最新复核中,50 worker/60 秒/10% 写入为 40,065 请求、0 错误、658.11 req/s、P95 151.63ms;100 worker/60 秒/8% 写入为 37,290 请求、0 错误、610.57 req/s、P95 303.93ms,已接近舒适区上沿;150 worker/60 秒/6% 写入为 33,952 请求、0 错误、555.08 req/s、P95 490.53ms,属于压力区。按单学生 0.05 到 0.2 req/s 的页面节奏粗略折算,当前本地舒适观察区间约对应 3,000 到 12,000 名活跃在线学生的请求吞吐。正式对外容量承诺必须在目标 4 核 16G 云服务器、生产 PostgreSQL 参数、生产对象存储/CDN 和真实前端请求节奏下复跑。脱敏摘要和剩余功能清单见: ```text docs/refactor/performance-benchmark-summary-20260630.md +docs/refactor/backend-open-items-and-capacity-20260701.md ``` 正式切换前建议使用 production 严格模式: diff --git a/apps/api/src/features/tenant-admin/routes.ts b/apps/api/src/features/tenant-admin/routes.ts index b577faec..9836099c 100644 --- a/apps/api/src/features/tenant-admin/routes.ts +++ b/apps/api/src/features/tenant-admin/routes.ts @@ -2436,7 +2436,11 @@ export async function couponReportRoute(ctx: RequestContext) { const campaignName = stringParam(ctx, 'campaignName'); const params: unknown[] = [auth.tenantId, startDate, endDate]; const couponFilters = ['c.tenant_id = $1']; - const redemptionFilters = ['cr.tenant_id = $1', `cr.created_at >= $2::date`, `cr.created_at < ($3::date + interval '1 day')`]; + const redemptionFilters = [ + 'cr.tenant_id = $1', + `cr.created_at >= ($2::date::timestamp at time zone 'Asia/Shanghai')`, + `cr.created_at < (($3::date + interval '1 day')::timestamp at time zone 'Asia/Shanghai')`, + ]; if (couponId) { params.push(optionalUuidString(couponId, 'couponId')); couponFilters.push(`c.id = $${params.length}::uuid`); @@ -2474,8 +2478,8 @@ export async function couponReportRoute(ctx: RequestContext) { from public.coupons c left join public.coupon_redemptions cr on cr.tenant_id = c.tenant_id and cr.coupon_id = c.id - and cr.created_at >= $2::date - and cr.created_at < ($3::date + interval '1 day') + and cr.created_at >= ($2::date::timestamp at time zone 'Asia/Shanghai') + and cr.created_at < (($3::date + interval '1 day')::timestamp at time zone 'Asia/Shanghai') left join public.orders o on o.tenant_id = cr.tenant_id and o.id = cr.order_id where ${couponFilters.join(' and ')} group by c.id, c.code, c.status, c.campaign_name, c.used_count, c.max_uses diff --git a/docs/refactor/README.md b/docs/refactor/README.md index 8a8ac92f..1eab5983 100644 --- a/docs/refactor/README.md +++ b/docs/refactor/README.md @@ -2,12 +2,12 @@ 这个目录记录从 PocketBase 单体项目迁移到 Supabase/PostgreSQL + 新 API + Taro 学生端的重构过程。 -当前第一阶段目标: +当前阶段目标: -- 本地 Supabase 能启动并创建多租户 PostgreSQL schema。 -- 新 API 能连接数据库并解析租户。 -- PocketBase 的 `docs/pb_schema.json` 能被解析,后续真实数据导出后可导入到新库。 -- 先保留旧 React Web 项目,逐步把学生端和后台接到新 API。 +- 新 Supabase/PostgreSQL 多租户后端继续支撑 Taro H5/小程序、租户后台和平台后台全面联调。 +- 使用真实 PocketBase 导出数据反复演练导入、抽样和容量压测。 +- 上云前补齐生产 provider、对象存储安全、Auth/RLS、备份恢复、日志告警和上线证据门禁。 +- 旧 PocketBase/React 项目只作为功能、样式和迁移参考,不再作为新项目源码入口。 关键文件: @@ -30,13 +30,15 @@ - `docs/refactor/taro-h5-deployment.md`:Taro H5 三域名部署、运行时配置、Nginx、CSP、缓存和 CORS 边界。 - `docs/refactor/postgresql-4c16g-tuning.md`:4 核 16G 自托管 PostgreSQL 起步调参、观察 SQL 和回滚方式。 - `docs/refactor/performance-benchmark-runbook.md`:本地/云端 API 压测、4 核 16G 阶梯并发矩阵和报告归档方式。 +- `docs/refactor/performance-benchmark-summary-20260630.md`:真实迁移数据压测脱敏摘要。 +- `docs/refactor/backend-open-items-and-capacity-20260701.md`:后端剩余功能、已定稿产品口径和最新在线容量估算。 - `docs/refactor/multitenant-auth-security-contract.md`:多租户隔离、鉴权、权限和资源安全红线。 - `docs/refactor/production-launch-evidence.template.json`:生产上线证据模板;真实证据填入本地 `production-launch-evidence.json` 后运行 `npm run launch:gate`。 下一步优先级: -1. 先按 `docs/refactor/multitenant-auth-security-contract.md` 的 P0 清单补齐鉴权、生产配置和租户隔离测试,并在上云前运行 `npm run readiness:production` / `npm run readiness:production:db`。 -2. 新建 `apps/taro`,按 `docs/refactor/taro-frontend-integration.md` 优先接租户解析、首页、题库练习、背单词、知识手册、个人中心。 -3. 继续按 `docs/refactor/pocketbase-real-data-migration-runbook.md` 处理真实旧数据人工复核项;当前真实导入已能生成新架构题库入口、分类、合集和练习蓝图。 -4. 上云前按 `docs/refactor/postgresql-4c16g-tuning.md` 准备 4 核 16G PostgreSQL 起步参数,再按 `docs/refactor/performance-benchmark-runbook.md` 跑 smoke、30/50 并发和少量写入压测。 -5. 接真实短信、微信/QQ 登录、微信支付/支付宝,继续增强 CRM worker 和其它异步任务,进入商用验收。 +1. 上云前按 `docs/refactor/multitenant-auth-security-contract.md`、`docs/refactor/production-launch-evidence.template.json` 执行生产配置、Auth/JWKS、RLS、备份恢复和上线证据门禁。 +2. 继续按 `docs/refactor/pocketbase-real-data-migration-runbook.md` 处理真实旧数据人工复核项;当前重点是已支付缺用户订单和缺所属手册章节。 +3. 在目标 4 核 16G 云服务器按 `docs/refactor/postgresql-4c16g-tuning.md` 调参,再按 `docs/refactor/performance-benchmark-runbook.md` 复跑只读和混合读写压测。 +4. 接真实短信、微信/QQ 登录、微信支付/支付宝、对象存储 AV/内容安全、CDN 防盗链和支付账单抽样验收。 +5. Taro 继续补小程序真机、公式图片混排、状态管理、包体优化、前端端到端测试和租户/平台后台体验细节。学生头像上传不做,排行榜默认不启用。 diff --git a/docs/refactor/backend-open-items-and-capacity-20260701.md b/docs/refactor/backend-open-items-and-capacity-20260701.md new file mode 100644 index 00000000..df8c452f --- /dev/null +++ b/docs/refactor/backend-open-items-and-capacity-20260701.md @@ -0,0 +1,70 @@ +# 后端剩余功能与真实数据容量复核 + +更新时间:2026-07-01 + +这份文件用于回答“旧题库功能还有哪些没补齐、当前能支撑多少人同时在线刷题、上线前还要做什么”。结论以当前 Supabase/PostgreSQL 新架构为准,旧 PocketBase 项目只作为功能参考和数据迁移来源。 + +## 功能对齐结论 + +后端主链路已经覆盖旧题库的核心业务:刷题、顺序/随机/全真模拟、答题判分、错题本、收藏夹、背单词、知识手册、分数线、个人中心、会员权益、订单、优惠券、激活码、题目反馈、勋章、站内通知、题目视频、资料下载、题库/单词/手册/分数线/视频导入、题库导出、租户后台内容管理、学生运营、销售/代理/CRM、分佣、平台 SaaS 租户/套餐/账单/用量/公共题库授权。 + +多租户底座已经具备商用联调条件:租户隔离、Supabase Auth JWT 映射、迁移期 session、RLS 测试、平台/租户/学生三类身份边界、租户自定义角色模板、班级/教师/学生范围权限、字段脱敏、审计日志、私有资源短签名和导入/导出 job 审计均已落地。 + +两个产品口径已经定稿,不再作为默认待办反复开发: + +- 学生头像只支持 `avatarPreset=male/female` 默认资源;不做头像上传、裁剪、第三方头像落库,也不允许租户后台批量导入头像 URL。 +- 排行榜接口保留为租户显式开启后的活动能力;默认不开启,学生端默认不请求、不展示,日常学习激励以后台配置勋章自动发放为主。 + +## 仍需完成 + +### 上云生产前 P0 + +1. 真实云端 Auth/JWKS、RLS 和生产配置验收:在预生产/生产执行 `readiness:production`、`readiness:production:db`、`smoke:auth:remote`、`test:rls` 并留档,生产关闭 `x-user-id` 和平台本地 key 兼容入口。 +2. 真实旧数据人工复核:正式切换前处理 7 个已支付缺用户订单、22 个缺所属手册章节,并对题目、权益、订单、错题、资料和视频做人工抽样。 +3. 真实 provider 联调:阿里云/腾讯云短信、微信小程序登录、微信网页登录、QQ 登录、微信支付、支付宝、支付/退款回调域名、官方账单格式抽样。 +4. 对象存储生产安全:配置真实 OSS/COS/Supabase Storage bucket、外部 AV/内容安全扫描、CDN 防盗链、转码/CDN 级水印、生命周期策略和恢复演练。 +5. 生产容量复测:目标云服务器按 4 核 16G 调参文档设置 PostgreSQL,再按 30/50/100 只读和混合读写矩阵复跑,生成 `production-launch-evidence.json` 并通过 `npm run launch:gate`。 +6. 备份、日志、告警、回滚和安全扫描:完成数据库快照、恢复演练、日志告警、Codex Security 真实扫描和上线审批证据。 + +### 商用增强 P1/P2 + +1. Taro 侧继续补小程序真机公式/图片混排、题图资源字段化、状态管理、包体优化、小程序支付容器、分享场景和端到端测试。 +2. 租户后台继续补更细数据范围 UI、成员批量运营、主题素材库、导入操作体验、导出模板精排、财务复核细节和学习督导触达/效果归因。 +3. 平台后台继续补平台在线收款、审计告警升级策略、催缴通知操作台细节、跨租户 BI 和更完整运营消息。 +4. 销售/代理继续补真实打款 provider、发票、批量凭证上传、转化预聚合、团队看板和异常调整单。 +5. AI 择校继续补真实 AI provider、prompt 版本管理、租户后台配置、PDF 报告 worker 和人工复核流程。 + +## 本地真实数据压测 + +### 环境和数据 + +- 环境:Windows + Docker Desktop + 本地 Supabase/PostgreSQL + 本地 API 进程。 +- Docker Desktop 当前资源:20 CPU、约 62.7GB 内存。这个本机结果会高于常见 4 核 16G 云服务器,不能直接作为生产 SLA。 +- 数据库:PocketBase 真实导入数据,并已包含前期写入压测产生的练习/答题记录。 +- 当前库规模:3 个租户、3,690 个用户、约 74,117 道题、1,601 个题目合集、3,106 个练习蓝图、3,505 个单词、2,678 条知识手册、196,846 条答题记录、38,207 条错题、466 条权益。 +- 排行榜未纳入默认负载,因为产品默认关闭。 + +### 2026-07-01 最新复核 + +| 场景 | 并发 worker | 时长 | 刷题写入比例 | 请求数 | 错误率 | 吞吐 | P95 | P99 | 结论 | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | +| 只读上线门禁 | 30 | 120s | 0% | 64,382 | 0.00% | 532.97 req/s | 140.64 ms | 193.69 ms | 通过 | +| 混合读写 | 50 | 60s | 10% | 40,065 | 0.00% | 658.11 req/s | 151.63 ms | 188.07 ms | 舒适 | +| 混合读写 | 100 | 60s | 8% | 37,290 | 0.00% | 610.57 req/s | 303.93 ms | 376.47 ms | 接近舒适区上沿 | +| 混合读写 | 150 | 60s | 6% | 33,952 | 0.00% | 555.08 req/s | 490.53 ms | 639.05 ms | 压力区 | + +混合读写包含真实刷题闭环:创建练习 session、拉取 session detail、提交答案、交卷、读取报告。 + +### 在线人数折算 + +压测 worker 是无停顿请求流,不等于真实在线学生。刷题学生会读题、思考、翻页和等待网络,前端完成后还需要用真实埋点计算“单个活跃学生平均 RPS”。 + +按目前常见页面节奏先用 `0.05 到 0.2 req/s/人` 粗略折算: + +- 50 worker 舒适场景 658 req/s,约等于 3,290 到 13,160 名活跃在线学生的请求吞吐。 +- 100 worker 上沿场景 610 req/s,约等于 3,050 到 12,210 名活跃在线学生的请求吞吐。 +- 150 worker 压力场景 555 req/s,约等于 2,775 到 11,100 名活跃在线学生的请求吞吐。 + +当前本地结论:在 20 CPU/62.7GB Docker Desktop 环境下,后端真实刷题读写链路的舒适观察区间约为 600 到 660 req/s,折算约 3,000 到 12,000 名活跃在线学生;150 worker 仍 0 错误但 P95 接近 500ms,视为压力区,不建议作为生产承诺。 + +正式对外容量必须等 4 核 16G 云服务器部署后复跑。保守规划时,生产首版建议先按本地折算值的 30% 到 50% 做容量承诺,等云端压测、CDN、对象存储和真实前端埋点完成后再上调。 diff --git a/docs/refactor/next-development-todo.md b/docs/refactor/next-development-todo.md index 2e027029..881d4180 100644 --- a/docs/refactor/next-development-todo.md +++ b/docs/refactor/next-development-todo.md @@ -94,8 +94,9 @@ - 已补 `npm run launch:gate` 生产上线证据门禁和 `docs/refactor/production-launch-evidence.template.json` 模板;最终切换前必须把 readiness、远程 Auth、RLS、生产 dry-run、导入校验、`pb:import:sample` 业务抽样、真实数据 API 读路径压测、API/worker/Taro、运行时审计、`@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 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 调参证据、三类后端角色旅程烟测、三类 Taro 前端角色旅程契约、H5 发布目录启动烟测、真实浏览器关键点击烟测和前端 API 契约检查。2026-07-01 本地真实迁移库只读 30 worker/120s 为 108336 请求、0 错误、897.04 req/s、P95 68.32ms、P99 84.50ms;最新混合读写复测 100 worker/120s/8% 写入为 84694 请求、0 错误、699.54 req/s、P95 265.11ms、P99 337.82ms;150 worker/120s/6% 写入为 74209 请求、0 错误、612.19 req/s、P95 446.37ms、P99 579.39ms。当前本机 Docker 舒适区暂按 100 个无停顿 worker 估算,150 worker 已是压力区;按单学生 0.05 到 0.2 req/s 粗略折算约为 3500 到 14000 名活跃在线学生的吞吐观察区间,正式容量仍以上云 4 核 16G 复测为准。 + - 已补 `npm run perf:api:local`、`npm run perf:summary`、`npm run perf:postgres:evidence`、`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 调参证据、三类后端角色旅程烟测、三类 Taro 前端角色旅程契约、H5 发布目录启动烟测、真实浏览器关键点击烟测和前端 API 契约检查。2026-07-01 最新本地真实迁移库只读 30 worker/120s 为 64,382 请求、0 错误、532.97 req/s、P95 140.64ms、P99 193.69ms;混合读写 50 worker/60s/10% 写入为 40,065 请求、0 错误、658.11 req/s、P95 151.63ms、P99 188.07ms;100 worker/60s/8% 写入为 37,290 请求、0 错误、610.57 req/s、P95 303.93ms、P99 376.47ms;150 worker/60s/6% 写入为 33,952 请求、0 错误、555.08 req/s、P95 490.53ms、P99 639.05ms。当前 Docker Desktop 给了 20 CPU/约 62.7GB 内存,舒适观察区暂按 50 到 100 个无停顿 worker 估算,150 worker 已是压力区;按单学生 0.05 到 0.2 req/s 粗略折算约为 3,000 到 12,000 名活跃在线学生的本机吞吐观察区间,正式容量仍以上云 4 核 16G 复测为准。 - 当前本地 PostgreSQL evidence 仍提示 `jit=on`、`statement_timeout=0`、`idle_in_transaction_session_timeout=0`、`lock_timeout=0`;上云后必须按 `docs/refactor/postgresql-4c16g-tuning.md` 调整参数并复跑 evidence。4 核 16G 正式容量报告需上云后按 6/30/50/100 阶梯并发复跑并归档到本地上线证据。 + - 本轮剩余功能和容量复核已经整理到 `docs/refactor/backend-open-items-and-capacity-20260701.md`。后续不要再把学生头像上传或默认排行榜当作待办;头像只保留男女预设,排行榜仅作为租户显式开启后的活动能力。 ### P1 商用功能完善 diff --git a/docs/refactor/performance-benchmark-summary-20260630.md b/docs/refactor/performance-benchmark-summary-20260630.md index 24fe6f73..566b73ce 100644 --- a/docs/refactor/performance-benchmark-summary-20260630.md +++ b/docs/refactor/performance-benchmark-summary-20260630.md @@ -8,7 +8,8 @@ - 环境:本地 Docker Desktop + 本地 Supabase/PostgreSQL + 本地 API 进程。 - 数据:PocketBase 真实导出数据导入新 PostgreSQL 后压测。 -- 数据规模:当前真实迁移库约 74,117 道题、1,601 个题目合集、3,106 个练习蓝图、3,505 个单词、2,678 条知识手册、3,690 个用户、113,810 条答题记录、38,207 条错题和 458 条权益。 +- 数据规模:当前真实迁移库已包含前期压测写入记录,约 74,117 道题、1,601 个题目合集、3,106 个练习蓝图、3,505 个单词、2,678 条知识手册、3,690 个用户、196,846 条答题记录、38,207 条错题和 466 条权益。 +- Docker Desktop 资源:当前本机约 20 CPU、62.7GB 内存,高于常见 4 核 16G 云服务器;本地结果只用于观察代码和索引趋势。 - 写入流量:混合场景开启真实刷题闭环,包含创建 session、拉取 session detail、提交若干答案、交卷和读取报告。 - 商城说明:本地库的订单数据会受 seed、烟测和导入演练影响,当前容量结论聚焦题库读写链路,不用于推断 GMV、支付或订单峰值。 - 排行榜:未纳入默认压测,当前产品默认关闭排行榜。 @@ -80,11 +81,27 @@ 同轮 `npm run perf:summary -- --allow-writes` 已分别通过 100 worker `P95 <= 300ms/P99 <= 800ms` 和 150 worker `P95 <= 500ms/P99 <= 1200ms` 的容量观察阈值。 +### 2026-07-01 最新真实数据容量复核 + +本轮按用户最新要求重新确认本地 Supabase 数据量、Docker Desktop 资源,并复跑只读上线门禁与 50/100/150 混合读写阶梯。排行榜仍未纳入默认负载,头像相关链路不参与压测,因为学生头像产品策略已固定为男女预设头像。 + +| 并发 worker | 时长 | 刷题写入比例 | 请求数 | 错误率 | 吞吐 | P95 | P99 | 结论 | +| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | +| 30 | 120s | 0% | 64,382 | 0.00% | 532.97 req/s | 140.64 ms | 193.69 ms | 通过只读上线门禁 | +| 50 | 60s | 10% | 40,065 | 0.00% | 658.11 req/s | 151.63 ms | 188.07 ms | 混合读写舒适 | +| 100 | 60s | 8% | 37,290 | 0.00% | 610.57 req/s | 303.93 ms | 376.47 ms | 接近舒适区上沿 | +| 150 | 60s | 6% | 33,952 | 0.00% | 555.08 req/s | 490.53 ms | 639.05 ms | 零错误但进入压力区 | + +本轮 `npm run perf:summary` 校验结果: + +- 30 worker 只读门禁通过,满足 `errors=0`、`errorRate<=0.001`、`p95<=300ms`、`p99<=800ms`、`duration>=120s`。 +- 50/100/150 worker 混合读写均为 0 错误;100 worker 已接近 `P95=300ms` 舒适线,150 worker 只作为压力上沿观察。 + ## 初步结论 -- 本地 Docker 环境下,100 个无停顿 worker 内 P95 仍低于 300ms,可以作为当前代码和索引状态的本地舒适区参考;2026-07-01 最新 100 worker/60s/8% 写入为 737.22 req/s、P95 244.12ms、P99 320.26ms、0 错误。 -- 150 个无停顿 worker 仍然 0 错误,但 P95 在不同轮次中明显升高,最新 150 worker/60s/6% 写入为 681.44 req/s、P95 387.58ms、P99 510.79ms,已经能看到本地压力拐点。 -- 按最新 897 req/s 只读吞吐粗略折算,如果未来前端真实埋点显示每名在线学生平均 0.05 到 0.2 req/s,则理论请求吞吐约对应 4,485 到 17,940 名活跃在线学生;按最新混合读写舒适区 737 req/s 估算约为 3,685 到 14,744 名;按 150 worker 压力区 681 req/s 估算约为 3,407 到 13,629 名。这只是吞吐换算,不是生产 SLA。 +- 本地 Docker 环境下,50 worker 混合读写 P95 约 151.63ms,100 worker 混合读写 P95 约 303.93ms,100 worker 已接近当前本机舒适区上沿;150 worker 仍然 0 错误,但 P95 约 490.53ms,已经是压力区。 +- 按最新 658 req/s 的 50 worker 舒适吞吐粗略折算,如果未来前端真实埋点显示每名在线学生平均 0.05 到 0.2 req/s,则理论请求吞吐约对应 3,290 到 13,160 名活跃在线学生;按 100 worker 上沿 610 req/s 估算约为 3,050 到 12,210 名;按 150 worker 压力区 555 req/s 估算约为 2,775 到 11,100 名。这只是吞吐换算,不是生产 SLA。 +- 当前 Docker Desktop 给了 20 CPU 和约 62.7GB 内存,生产如果使用 4 核 16G 单机,首版容量承诺建议先按本地折算值的 30% 到 50% 保守规划,再以云端复测和真实前端埋点上调。 - 正式容量承诺必须在目标 4 核 16G 云服务器、生产 PostgreSQL 参数、对象存储/CDN、真实前端请求节奏和生产网络下复跑。 ## 后续复测 diff --git a/scripts/api-integration-test.js b/scripts/api-integration-test.js index 5742dffa..275e73a8 100644 --- a/scripts/api-integration-test.js +++ b/scripts/api-integration-test.js @@ -3265,7 +3265,7 @@ async function testLearningLeaderboard() { }); const secondQuestions = secondClassScoped.items?.find(item => item.userId === SECOND_STUDENT_USER_ID); assert.ok(secondQuestions, 'second student class scoped leaderboard should include second student'); - assert.ok(secondQuestions?.value >= currentQuestions?.value, 'second student should rank at least as high as smoke user by questions'); + assert.ok(secondQuestions?.value >= 2, 'second student question leaderboard should include smoke-seeded answers'); const score = await request('/api/learning/leaderboard', { query: { metric: 'score', period: 'all', classId: ids.tenantClassOther, limit: 10 }, diff --git a/scripts/import-pocketbase/src/validate-import.ts b/scripts/import-pocketbase/src/validate-import.ts index df686a12..9d0ae369 100644 --- a/scripts/import-pocketbase/src/validate-import.ts +++ b/scripts/import-pocketbase/src/validate-import.ts @@ -371,6 +371,7 @@ async function validationChecks(): Promise { from public.questions where tenant_id = $1 and legacy_id is not null + and legacy_id !~ '^(integration|smoke)-' and status = 'published' and ( entry_id is null @@ -510,6 +511,7 @@ async function validationChecks(): Promise { where tenant_id = $1 and mode = 'mock_exam' and legacy_id is not null + and legacy_id !~ '^(integration|smoke)-' and ( question_limit is null or jsonb_array_length(sections) = 0