perf-debug
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePerf debug (front → back)
性能调试(前端 → 后端)
Repeatable loop for resolving Kodus web performance problems, from the rendered
screen down to the DB query. Built from real sessions on the token-usage,
pull-requests, settings and cockpit screens.
用于解决 Kodus Web 性能问题的可重复流程,贯穿从渲染后的页面到 DB 查询。该流程基于对 token-usage、pull-requests、settings 和 cockpit 页面的实际调试会话沉淀而成。
The loop
调试循环
-
Open the screen — Playwright () or Chrome MCP. App is auth-gated;
mcp__plugin_playwright_playwright__*is 404 post-Next16, log in at/(email → Continue → password). Test user:/sign-in(orgNovus@teste.comhas real data). Reset its password via Postgres if needed (bcryptjs hash; see the memory).aae1c003-… -
Observe — never trust the first load. Capture(errors) +
browser_console_messages(fan-out). Reload 2× on a settled dev server: HMR produces phantom hydration errors / truncated-parse 500s. Only what reproduces on a clean load is real.browser_network_requests -
Measure via the Performance API ():
browser_evaluate(TTFB), FCP, LCP, load. Classify the bottleneck:nav.responseStart- high TTFB → SSR-data-bound (server component awaiting slow data)
- big JS / slow FCP after TTFB → bundle-bound
- janky interaction → render-bound (note: React Compiler is ON, so manual memo is rarely the fix).
-
Understand the components — read the page.tsx / client component. Find the server/client split and the loading states. Watch for(blanks the body pre-hydration → show a skeleton instead) and effects that
if (!isMounted) return nullwithrouter.replacein deps (reload loops).searchParams -
Trace to the API — from the fan-out, pick the heavy/redundant endpoints (duplicates, per-page,
/api/auth/session, cockpit fan-out)./executions -
Understand the query — read→
apps/api/src/controllers/*→ repository. Then hit the DB directly:libs/**/use-cases- Postgres: , then
docker exec kodus_api printenv API_PG_DB_PASSWORD. Checkdocker exec -e PGPASSWORD=… db_postgres psql -U kodusdev -d kodus_dbfor the table; look for OFFSET-in-loop, uncachedpg_indexes, N+1 (COUNT(*)on a to-one is a JOIN, not N+1).relations:[...] - Mongo: .
docker exec mongodb mongosh "mongodb://kodusdev:<pass>@localhost:27017/<db>?authSource=admin"(watch for partial indexes matching the filter),db.<coll>.getIndexes()— compare.explain("executionStats")vstotalKeysExamined. See thenReturnedskill for deeper analysis.mongodb-query-optimizer - Kodus DB is generally well-indexed; the real backend wins are usually algorithmic (keyset vs OFFSET) or caching — and only reproduce at prod scale (dev DB is tiny), so prepare the patch and validate in staging.
- Postgres:
-
Fix surgically + verify live — one change, then re-measure + re-check console on a clean reload. Confirm no new hydration errors, no 500.
-
Isolate when unsure — revert to compare. If a change might be the cause,(back it up first) and reload: if the symptom persists with the original, your change is exonerated. (Used to prove the token-usage blank was a pre-existing reload loop, not the recharts port.)
git checkout -- <file>
-
打开页面——使用 Playwright()或 Chrome MCP。应用需要身份验证;Next16 之后
mcp__plugin_playwright_playwright__*返回 404,需在/登录(email → Continue → password)。测试用户:/sign-in(组织Novus@teste.com中有真实数据)。如有必要,可通过 Postgres 重置其密码(bcryptjs hash;参见 memory)。aae1c003-… -
观察——绝不要相信首次加载。 捕获(错误)以及
browser_console_messages(请求扇出)。在开发服务器稳定后重新加载 2 次:HMR 会产生虚假的 hydration 错误或因截断解析导致的 500 错误。只有在干净加载时能够复现的问题才是真问题。browser_network_requests -
测量,通过 Performance API():
browser_evaluate(TTFB)、FCP、LCP、load。判断瓶颈类型:nav.responseStart- TTFB 过高 → SSR-data-bound(服务端组件正在等待缓慢的数据)
- JS 体积过大 / TTFB 后 FCP 缓慢 → bundle-bound
- 交互卡顿 → render-bound(注意:React Compiler 已开启,因此手动 memo 很少是正确的修复方式)。
-
理解组件——阅读 page.tsx / client component。找出服务端与客户端的拆分方式以及 loading state。注意(在 hydration 前清空正文 → 应改为显示 skeleton),以及在依赖项中包含
if (!isMounted) return null时调用searchParams的 effects(会导致重复加载循环)。router.replace -
追踪至 API——从请求扇出中找出耗时较高或冗余的 endpoint(重复请求、逐页调用、
/api/auth/session、cockpit 请求扇出)。/executions -
理解查询——阅读→
apps/api/src/controllers/*→ repository。然后直接连接 DB:libs/**/use-cases- Postgres:,然后运行
docker exec kodus_api printenv API_PG_DB_PASSWORD。检查表对应的docker exec -e PGPASSWORD=… db_postgres psql -U kodusdev -d kodus_db;查找循环中使用 OFFSET、未缓存的pg_indexes、N+1(to-one 关系上的COUNT(*)是 JOIN,而不是 N+1)。relations:[...] - Mongo:。使用
docker exec mongodb mongosh "mongodb://kodusdev:<pass>@localhost:27017/<db>?authSource=admin"(留意与过滤条件匹配的部分索引),并使用db.<coll>.getIndexes()——比较.explain("executionStats")与totalKeysExamined。如需更深入的分析,请参阅nReturnedskill。mongodb-query-optimizer - Kodus DB 的索引通常已经比较完善;后端真正的性能收益通常来自算法优化(keyset 而非 OFFSET)或缓存——而且只有按生产规模复现(dev DB 的数据量很小)才能观察到真实差异,因此应准备补丁并在 staging 中验证。
- Postgres:
-
精准修复并进行实时验证——每次只做一项更改,然后重新测量,并在干净重载后重新检查 console。确认没有新的 hydration 错误,也没有 500 错误。
-
不确定时进行隔离——回退后对比。 如果某项更改可能是原因,先备份文件,再执行并重新加载:如果恢复原始版本后症状仍然存在,即可排除此次更改是原因。(曾使用该方法证明 token-usage 页面空白源于已有的重复加载循环,而不是 recharts 移植所致。)
git checkout -- <file>
Dev-env gotchas (these cost hours if unknown)
开发环境陷阱(如果不了解这些问题,可能会耗费数小时)
- HMR noise → hydration/parse errors that vanish on a 2nd clean reload.
- Turbopack truncated-parse cache (on a valid file) →
Unexpected eofthe file to force a fresh re-read. Root cause:touchCHOKIDAR_USEPOLLING=false- bind-mount catches partial writes.
:delegated
- OOM → heavy routes (charts + turbopack) blow the web container's memory
limit. Check /
docker stats; bumpdocker inspect --format '{{.State.OOMKilled}}'(web needed 4G, 2G OOM'd).deploy.resources.limits.memory - Reload loop → a page hammering its own document (×1000s, CPU pegged, blank body). Diagnose via request count in
GET /x. Cause is usually an effect that navigates with the value it depends on in its deps.docker logs - Cache masks slow loads — the token-usage is ~7s cold / ~400ms cached; bust the cache (uncached filter combo) to observe the real load.
$facet - Healthcheck false-unhealthy — pings
kodus_web(404 post-Next16) so it shows "unhealthy" while working./
- HMR 噪声 → hydration/解析错误会在第二次干净重载后消失。
- Turbopack 截断解析缓存(有效文件出现 )→
Unexpected eof该文件以强制重新读取。根因是touch+CHOKIDAR_USEPOLLING=falsebind mount 捕获到了部分写入。:delegated - OOM → 重量级路由(charts + Turbopack)会超出 web 容器的内存限制。检查 /
docker stats;提高docker inspect --format '{{.State.OOMKilled}}'(web 容器需要 4G,2G 时会 OOM)。deploy.resources.limits.memory - 重复加载循环 → 页面会疯狂请求自身的 document(× 1000 次,CPU 跑满,正文空白)。可通过
GET /x中的请求数量进行诊断。常见原因是某个 effect 在导航时使用了它所依赖的值,并将该值放入 deps。docker logs - 缓存会掩盖慢加载——token-usage 的 冷加载约需 7 秒,缓存命中时约需 400 毫秒;请绕过缓存(使用未缓存的过滤条件组合)以观察真实加载耗时。
$facet - 健康检查误报不健康——会探测
kodus_web(Next16 之后返回 404),因此即使应用正常运行也会显示 "unhealthy"。/
Reference
参考
See memory for the condensed version and
for the concrete fixes shipped with this flow.
reference_perf_debug_flowproject_frontend_perf_next16有关该流程的精简版,请参阅 memory ;有关该流程已实际交付的具体修复,请参阅 。
reference_perf_debug_flowproject_frontend_perf_next16