docs(test): record login-load baseline against Vite and deployed dev
The current workbench stays at one member fetch across 20 logins, while the deployed bundle still fans out. Slow-network samples stay diagnostic until the container is rebuilt. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -49,7 +49,7 @@ updated: 2026-09-19
|
||||
| FE App.tsx | 部分。登录壳不再静态引用重模块。本地缓存、登录同步、文档补水、服务端文档同步、生命周期、发送消息、块编辑、对话流/任务流重试、卡片动作、立项和成员写操作已分别抽出。工作台壳仍在 `App.tsx` |
|
||||
| H1-del | 关闭理由:`allowLegacyTestPrincipal` 仍被 `AllowInMemory && ContextDbHost==""` 的 dev/test 内存测试使用。删除会把这批测试变成未登录 401。保留告警,删除另开、先改测试夹具 |
|
||||
|
||||
仍开放:rdev P3 三样本真 PI、首次加载 U1/U6/U7、dev 容器重建与治理库 PG。公式引擎已离开入口包(`index-j0BIrjIR.js` 737.38 kB,br 170,451 B,包内无 katex),U3 仍缺浏览器瀑布,不关。P4 键名空间化已在本分支落地:新清单用带前缀的任务键,旧键与旧 doc_id 仍可读,`TestPublishedTaskKeysAreNamespacedAndDisjoint`。意图短语仍输出旧键,执行路径按别名接上。P2 规划界面已折叠长阶段、可批量纳入人工审批,并在选中 rdev 时提示 CDO 前置。U4 登录扇出收口仍有效,浏览器请求次数证据还没有。2026-09-19 在 `fix/audit-remainder-closeout` 跑过 `cd web && npm test && npm run build`:node:test 414 pass / 0 fail,vitest 32 pass;入口 `index-BFf6n8IA.js` 1,005.01 kB,br 235,285 B。CURRENT 文首 schema 52 与三套容器一致,00053 要等重建后才升。H2 批注/审计的项目成员检查在 Postgres 上走 `RequireProjectAccess`;内存授权器在无库时把任意正用户 id 视为成员,这是 `TestAuthorizeDocRevision_ReviewerAndNonMemberCannotWrite` 的隔离前提,不能改成默认拒绝。H2 的 HTTP 负例因此要在 `TEST_DATABASE_URL` 上补,本机未设置该变量。M7 的投影意图已随账本提交(goose `00053`);容器 schema 仍是 52,要等占槽重建后才升到 53。
|
||||
仍开放:rdev P3 三样本真 PI、首次加载 U1 慢网/U6/U7、dev 容器重建与治理库 PG。公式引擎已离开入口包(`index-j0BIrjIR.js` 737.38 kB,br 170,451 B,包内无 katex),U3 在本仓 Vite 登录瀑布里未见 katex/mermaid/docx,但生产包瀑布要等重建后复测,不关。P4 键名空间化已在本分支落地:新清单用带前缀的任务键,旧键与旧 doc_id 仍可读,`TestPublishedTaskKeysAreNamespacedAndDisjoint`。意图短语仍输出旧键,执行路径按别名接上。P2 规划界面已折叠长阶段、可批量纳入人工审批,并在选中 rdev 时提示 CDO 前置。U4 源码收口在 Vite n=20 上成立(成员项目 1、会话列表 1);已部署 8080 旧包仍是 704/30,浏览器关账要等重建。2026-09-19 在 `fix/audit-remainder-closeout` 跑过 `cd web && npm test && npm run build`:node:test 414 pass / 0 fail,vitest 32 pass;入口 `index-BFf6n8IA.js` 1,005.01 kB,br 235,285 B。CURRENT 文首 schema 52 与三套容器一致,00053 要等重建后才升。H2 批注/审计的项目成员检查在 Postgres 上走 `RequireProjectAccess`;内存授权器在无库时把任意正用户 id 视为成员,这是 `TestAuthorizeDocRevision_ReviewerAndNonMemberCannotWrite` 的隔离前提,不能改成默认拒绝。H2 的 HTTP 负例因此要在 `TEST_DATABASE_URL` 上补,本机未设置该变量。M7 的投影意图已随账本提交(goose `00053`);容器 schema 仍是 52,要等占槽重建后才升到 53。
|
||||
|
||||
红线关闭(不是实现):
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
|
||||
## 2026-09-17 首次加载性能专项
|
||||
|
||||
首次加载与重启首访性能优化:**partial**(U2 静态交付已验收;U3 只做了登录壳拆包,未关闭)。[审计](../audit/2026-09-17-initial-load-performance-audit.md) · [方案](../remediation/2026-09-17-initial-load-performance-plan.md) · [U2 报告](../test-reports/2026-09-17-initial-load-perf-u2-test-report.md)。U2:自托管字体 + br/gzip。U3 进展(`fix/audit-remainder-closeout`):导出、预览、管理台、块编辑器、Mermaid/公式改为点击或出现时再加载。入口包 `index-*.js` 由 2.83 MB / br 626 KB 降到 737 kB / br 170 KB(`index-j0BIrjIR.js`,包内无 katex)。公式在气泡或正文里出现时才加载,富文本复制同样延后。U3 的浏览器资源瀑布还没测,本行不关。U5:`/auth/me` 复用本请求已核过的用户,不再第二次解析会话;会话与用户状态合成一次 JOIN。没有锁等待证据,所以不节流 `last_seen`。`TestAuthMe_LogoutNextRequestIs401`。U1、U6、U7 未做。U4 进展:登录不再按项目数拉成员名册,只同步当前项目;其他项目先种入当前用户以保持会话可见,完整名册在打开成员面板时取。会话列表不再因项目数组变化而重拉,迟到响应用代次丢弃,同一项目的在途成员请求合并。`src/data/initFanout.test.ts`。尚未用浏览器记录 1/30/100 项目的真实请求次数。mutex `web + static-delivery`(U2 done)/ `loading-performance-audit`。
|
||||
首次加载与重启首访性能优化:**partial**(U2 静态交付已验收;U3 只做了登录壳拆包,未关闭)。[审计](../audit/2026-09-17-initial-load-performance-audit.md) · [方案](../remediation/2026-09-17-initial-load-performance-plan.md) · [U2 报告](../test-reports/2026-09-17-initial-load-perf-u2-test-report.md)。U2:自托管字体 + br/gzip。U3 进展(`fix/audit-remainder-closeout`):导出、预览、管理台、块编辑器、Mermaid/公式改为点击或出现时再加载。入口包 `index-*.js` 由 2.83 MB / br 626 KB 降到 737 kB / br 170 KB(`index-j0BIrjIR.js`,包内无 katex)。公式在气泡或正文里出现时才加载,富文本复制同样延后。U3 的浏览器资源瀑布还没测,本行不关。U5:`/auth/me` 复用本请求已核过的用户,不再第二次解析会话;会话与用户状态合成一次 JOIN。没有锁等待证据,所以不节流 `last_seen`。`TestAuthMe_LogoutNextRequestIs401`。U1、U6、U7 未做。U4 进展:登录不再按项目数拉成员名册,只同步当前项目;其他项目先种入当前用户以保持会话可见,完整名册在打开成员面板时取。会话列表不再因项目数组变化而重拉,迟到响应用代次丢弃,同一项目的在途成员请求合并。`src/data/initFanout.test.ts`。2026-09-19 浏览器证据(`e2e/initial-load-performance.spec.ts`,n=20,本仓 Vite `:3100` 代理 `:8080`):每次登录成员项目数 1、会话列表 GET 1,匿名与登录后资源路径均无 katex/mermaid/docx。同一探针打在已部署 `:8080`(旧包 `index-DhyNmxJE.js`,n=1)仍是成员项目 704、会话列表 GET 30,所以 U4 不能在容器重建前关掉。U1 本地登录态已有样本,10/2 Mbps 与 CPU 4 倍、U6、U7 仍未关。mutex `web + static-delivery`(U2 done)/ `loading-performance-audit`。
|
||||
|
||||
## 2026-09-17 文档编制人兼任方案
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# 首次加载 U1 登录态基线 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
[首次加载方案](../remediation/2026-09-17-initial-load-performance-plan.md) 的 U1 要求登录态瀑布,不能用未登录测量代替。此前只完成未登录诊断。本轮在 `fix/audit-remainder-closeout` 补登录探针,核对 U4 成员扇出与 U3 重模块是否出现在首屏。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 登录后记录工作台就绪时间、资源摘要、成员 GET 与会话列表 GET。
|
||||
- 证据去掉查询串,不保存令牌和正文。
|
||||
- 不把 n=1 或本机延迟写成预算达标。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- OS:Linux;浏览器:`mcr.microsoft.com/playwright:v1.63.0-noble` headless Chromium,`--network host`。
|
||||
- 入口 A:已部署 dev `http://127.0.0.1:8080`(容器内旧包,schema 52)。
|
||||
- 入口 B:本仓 Vite `http://127.0.0.1:3100`,API 代理到 8080。
|
||||
- 账号:容器环境变量中的管理员,不写入报告。
|
||||
- 用例:`web/e2e/initial-load-performance.spec.ts`。每次新 context,不发送聊天消息。
|
||||
- 未覆盖:每场景 20 次的分位数、字体黑洞单独对照、U7 重启矩阵。n=3 的慢网结果只作诊断,不能关预算。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- 当前源码:登录成员同步只打当前项目,会话列表每个查询键成功后不再被成员更新重拉。
|
||||
- 已部署旧包:预期仍按项目数扇出。该结果只说明容器落后,不说明源码回退。
|
||||
- 匿名首屏资源路径不应包含 katex、mermaid、docx。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
- **测试输入 (Input)**:`RUN_INITIAL_LOAD_BASELINE=1`,Vite `INITIAL_LOAD_SAMPLES=20` 且 `EXPECT_INIT_MEMBER_PROJECTS=1`;8080 同样探针 n=1。
|
||||
- **实际输出 (Actual Output)**:
|
||||
|
||||
```text
|
||||
vite-3100.json samples 20
|
||||
member projects 1 1
|
||||
session gets 1 1
|
||||
ready ms 118 152
|
||||
anon visible ms 148 194
|
||||
heavy []
|
||||
anonymous summary requestCount 170 transferBytes 6918643 jsTransferBytes 2349690 jsCount 8
|
||||
|
||||
deployed-8080.json samples 1
|
||||
memberGetCount 704
|
||||
distinctMemberProjects 704
|
||||
sessionListGetCount 30
|
||||
anonymous jsTransferBytes 626281 path /assets/index-DhyNmxJE.js
|
||||
workbenchReadyMs 181
|
||||
```
|
||||
|
||||
- **评价判定 (Evaluation / Verdict)**:PASS(探针与 U4 源码行为)。Vite 20/20 为成员项目 1、会话列表 1,重模块未进首屏。FAIL(关账):8080 旧包仍 704/30;慢网与重启未测;Vite 170 请求 / 6.9 MB 是开发模式,不套生产体积预算。
|
||||
- **性能与耗时 (Duration & Latency)**:20 次 Vite 登录工作台就绪 118–152 ms(本机、无带宽限制,不是预算验收)。8080 慢网 n=3、CPU 4 倍、并阻断 Google 字体域名:10 Mbps 登录表单可见 1169/1207/1238 ms;2 Mbps 为 3358/3424/3407 ms。登录点击到工作台约 328–373 ms,因为静态资源已在匿名导航中取完。2 Mbps 表单可见超过拟定 3 s 预算,且样本不足 20,不关 U1。
|
||||
|
||||
证据:[assets/2026-09-19-initial-load-u1/](assets/2026-09-19-initial-load-u1/)。
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
相关单元测试:
|
||||
|
||||
```text
|
||||
✔ freeform document actions live in one hook
|
||||
✔ redactResourceUrl drops query and origin
|
||||
✔ summarizeResources flags deferred heavy chunks
|
||||
✔ countInitRequests separates member fanout from session lists
|
||||
✔ percentile uses nearest-rank and stays null on empty input
|
||||
ℹ pass 5 fail 0
|
||||
```
|
||||
|
||||
Playwright:上述基线 1 passed。本报告不把未改动的 `go test ./...` 写成这次探针的验收。
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
登录态基线已经能回答「耗时之后请求打到哪里」:已部署容器的主成本是 704 次成员 GET,当前源码已收成 1 次,但要占槽重建后在 8080 复测才能关 U4。U6 不在这次加索引:测到的瓶颈是未部署的扇出,不是一条慢 SQL。下一步是慢网/CPU 样本、U7 隔离重启,以及 dev 容器重建使 schema 升到 00053。
|
||||
@@ -0,0 +1,62 @@
|
||||
[
|
||||
{
|
||||
"sample": 1,
|
||||
"anonymousVisibleMs": 170,
|
||||
"workbenchReadyMs": 181,
|
||||
"anonymous": {
|
||||
"navigation": [
|
||||
{
|
||||
"ttfb": 1.2000000476837158,
|
||||
"dcl": 133.20000004768372,
|
||||
"load": 134.10000002384186
|
||||
}
|
||||
],
|
||||
"paint": [
|
||||
{
|
||||
"name": "first-paint",
|
||||
"ms": 28
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"requestCount": 4,
|
||||
"transferBytes": 684047,
|
||||
"jsTransferBytes": 626281,
|
||||
"jsCount": 1,
|
||||
"heavyChunks": []
|
||||
},
|
||||
"longTaskCount": 1
|
||||
},
|
||||
"loggedIn": {
|
||||
"navigation": [
|
||||
{
|
||||
"ttfb": 1.2000000476837158,
|
||||
"dcl": 133.20000004768372,
|
||||
"load": 134.10000002384186
|
||||
}
|
||||
],
|
||||
"paint": [
|
||||
{
|
||||
"name": "first-paint",
|
||||
"ms": 28
|
||||
},
|
||||
{
|
||||
"name": "first-contentful-paint",
|
||||
"ms": 164
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"requestCount": 250,
|
||||
"transferBytes": 1214378,
|
||||
"jsTransferBytes": 626281,
|
||||
"jsCount": 1,
|
||||
"heavyChunks": []
|
||||
},
|
||||
"longTaskCount": 1
|
||||
},
|
||||
"initRequests": {
|
||||
"memberGetCount": 704,
|
||||
"distinctMemberProjects": 704,
|
||||
"sessionListGetCount": 30
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"profile": "10mbps-cpu4",
|
||||
"sample": 1,
|
||||
"anonymousVisibleMs": 1207,
|
||||
"workbenchReadyMs": 349
|
||||
},
|
||||
{
|
||||
"profile": "10mbps-cpu4",
|
||||
"sample": 2,
|
||||
"anonymousVisibleMs": 1169,
|
||||
"workbenchReadyMs": 328
|
||||
},
|
||||
{
|
||||
"profile": "10mbps-cpu4",
|
||||
"sample": 3,
|
||||
"anonymousVisibleMs": 1238,
|
||||
"workbenchReadyMs": 333
|
||||
},
|
||||
{
|
||||
"profile": "2mbps-cpu4",
|
||||
"sample": 1,
|
||||
"anonymousVisibleMs": 3358,
|
||||
"workbenchReadyMs": 343
|
||||
},
|
||||
{
|
||||
"profile": "2mbps-cpu4",
|
||||
"sample": 2,
|
||||
"anonymousVisibleMs": 3424,
|
||||
"workbenchReadyMs": 373
|
||||
},
|
||||
{
|
||||
"profile": "2mbps-cpu4",
|
||||
"sample": 3,
|
||||
"anonymousVisibleMs": 3407,
|
||||
"workbenchReadyMs": 342
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -157,3 +157,37 @@ test('logged-in workbench records waterfall and init request counts', async ({ b
|
||||
expect(last?.initRequests.distinctMemberProjects).toBeLessThanOrEqual(Number(memberCap));
|
||||
}
|
||||
});
|
||||
|
||||
test('throttled login still reaches the workbench', async ({ browser }) => {
|
||||
test.skip(process.env.INITIAL_LOAD_THROTTLE !== '1', 'set INITIAL_LOAD_THROTTLE=1 to measure slow-network budgets');
|
||||
if (!username || !password) {
|
||||
throw new Error('ADMIN_USERNAME and ADMIN_PASSWORD are required');
|
||||
}
|
||||
mkdirSync(evidenceDir, { recursive: true });
|
||||
const profiles = [
|
||||
{ name: '10mbps-cpu4', download: 1_250_000, upload: 625_000 },
|
||||
{ name: '2mbps-cpu4', download: 250_000, upload: 125_000 },
|
||||
];
|
||||
const rows = [];
|
||||
for (const profile of profiles) {
|
||||
for (let sample = 1; sample <= samples; sample += 1) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const client = await context.newCDPSession(page);
|
||||
await client.send('Network.emulateNetworkConditions', {
|
||||
offline: false,
|
||||
latency: 80,
|
||||
downloadThroughput: profile.download,
|
||||
uploadThroughput: profile.upload,
|
||||
});
|
||||
await client.send('Emulation.setCPUThrottlingRate', { rate: 4 });
|
||||
await page.route(/fonts\.(googleapis|gstatic)\.com/, (route) => route.abort());
|
||||
const anonymousVisibleMs = await openAnonymous(page);
|
||||
const workbenchReadyMs = await login(page);
|
||||
rows.push({ profile: profile.name, sample, anonymousVisibleMs, workbenchReadyMs });
|
||||
writeFileSync(path.join(evidenceDir, 'throttled.json'), JSON.stringify(rows, null, 2));
|
||||
await context.close();
|
||||
}
|
||||
}
|
||||
expect(rows.every((row) => row.workbenchReadyMs > 0)).toBe(true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user