|
| 1 | +--- |
| 2 | +name: perf-debug |
| 3 | +description: End-to-end front→back performance debugging for the Kodus web app. Use when a screen is slow, blank, looping, or you need to trace a UI perf problem down to the API query. Drives Playwright/Chrome MCP to open + measure the screen, then reads the API use-case/repository and the DB (Postgres/Mongo indexes, explain) to find and fix the root cause, verifying live. |
| 4 | +--- |
| 5 | + |
| 6 | +# Perf debug (front → back) |
| 7 | + |
| 8 | +Repeatable loop for resolving Kodus web performance problems, from the rendered |
| 9 | +screen down to the DB query. Built from real sessions on the token-usage, |
| 10 | +pull-requests, settings and cockpit screens. |
| 11 | + |
| 12 | +## The loop |
| 13 | + |
| 14 | +1. **Open the screen** — Playwright (`mcp__plugin_playwright_playwright__*`) or |
| 15 | + Chrome MCP. App is auth-gated; `/` is 404 post-Next16, log in at `/sign-in` |
| 16 | + (email → Continue → password). Test user: `Novus@teste.com` (org |
| 17 | + `aae1c003-…` has real data). Reset its password via Postgres if needed |
| 18 | + (bcryptjs hash; see the memory). |
| 19 | + |
| 20 | +2. **Observe — never trust the first load.** Capture `browser_console_messages` |
| 21 | + (errors) + `browser_network_requests` (fan-out). **Reload 2× on a settled |
| 22 | + dev server**: HMR produces phantom hydration errors / truncated-parse 500s. |
| 23 | + Only what reproduces on a clean load is real. |
| 24 | + |
| 25 | +3. **Measure** via the Performance API (`browser_evaluate`): |
| 26 | + `nav.responseStart` (TTFB), FCP, LCP, load. Classify the bottleneck: |
| 27 | + - high TTFB → **SSR-data-bound** (server component awaiting slow data) |
| 28 | + - big JS / slow FCP after TTFB → **bundle-bound** |
| 29 | + - janky interaction → **render-bound** (note: React Compiler is ON, so |
| 30 | + manual memo is rarely the fix). |
| 31 | + |
| 32 | +4. **Understand the components** — read the page.tsx / client component. Find the |
| 33 | + server/client split and the loading states. Watch for `if (!isMounted) return |
| 34 | + null` (blanks the body pre-hydration → show a skeleton instead) and effects |
| 35 | + that `router.replace` with `searchParams` in deps (reload loops). |
| 36 | + |
| 37 | +5. **Trace to the API** — from the fan-out, pick the heavy/redundant endpoints |
| 38 | + (duplicates, per-page `/api/auth/session`, `/executions`, cockpit fan-out). |
| 39 | + |
| 40 | +6. **Understand the query** — read `apps/api/src/controllers/*` → |
| 41 | + `libs/**/use-cases` → repository. Then hit the DB directly: |
| 42 | + - Postgres: `docker exec kodus_api printenv API_PG_DB_PASSWORD`, then |
| 43 | + `docker exec -e PGPASSWORD=… db_postgres psql -U kodusdev -d kodus_db`. |
| 44 | + Check `pg_indexes` for the table; look for OFFSET-in-loop, uncached |
| 45 | + `COUNT(*)`, N+1 (`relations:[...]` on a to-one is a JOIN, not N+1). |
| 46 | + - Mongo: `docker exec mongodb mongosh "mongodb://kodusdev:<pass>@localhost:27017/<db>?authSource=admin"`. |
| 47 | + `db.<coll>.getIndexes()` (watch for **partial indexes** matching the |
| 48 | + filter), `.explain("executionStats")` — compare `totalKeysExamined` vs |
| 49 | + `nReturned`. See the `mongodb-query-optimizer` skill for deeper analysis. |
| 50 | + - Kodus DB is generally **well-indexed**; the real backend wins are usually |
| 51 | + algorithmic (keyset vs OFFSET) or caching — and only reproduce at prod |
| 52 | + scale (dev DB is tiny), so prepare the patch and validate in staging. |
| 53 | + |
| 54 | +7. **Fix surgically + verify live** — one change, then re-measure + re-check |
| 55 | + console on a clean reload. Confirm no new hydration errors, no 500. |
| 56 | + |
| 57 | +8. **Isolate when unsure — revert to compare.** If a change might be the cause, |
| 58 | + `git checkout -- <file>` (back it up first) and reload: if the symptom |
| 59 | + persists with the original, your change is exonerated. (Used to prove the |
| 60 | + token-usage blank was a pre-existing reload loop, not the recharts port.) |
| 61 | + |
| 62 | +## Dev-env gotchas (these cost hours if unknown) |
| 63 | + |
| 64 | +- **HMR noise** → hydration/parse errors that vanish on a 2nd clean reload. |
| 65 | +- **Turbopack truncated-parse cache** (`Unexpected eof` on a valid file) → |
| 66 | + `touch` the file to force a fresh re-read. Root cause: `CHOKIDAR_USEPOLLING=false` |
| 67 | + + `:delegated` bind-mount catches partial writes. |
| 68 | +- **OOM** → heavy routes (charts + turbopack) blow the web container's memory |
| 69 | + limit. Check `docker stats` / `docker inspect --format '{{.State.OOMKilled}}'`; |
| 70 | + bump `deploy.resources.limits.memory` (web needed 4G, 2G OOM'd). |
| 71 | +- **Reload loop** → a page hammering its own document (`GET /x` ×1000s, CPU |
| 72 | + pegged, blank body). Diagnose via request count in `docker logs`. Cause is |
| 73 | + usually an effect that navigates with the value it depends on in its deps. |
| 74 | +- **Cache masks slow loads** — the token-usage `$facet` is ~7s cold / ~400ms |
| 75 | + cached; bust the cache (uncached filter combo) to observe the real load. |
| 76 | +- **Healthcheck false-unhealthy** — `kodus_web` pings `/` (404 post-Next16) so |
| 77 | + it shows "unhealthy" while working. |
| 78 | + |
| 79 | +## Reference |
| 80 | +See memory `reference_perf_debug_flow` for the condensed version and |
| 81 | +`project_frontend_perf_next16` for the concrete fixes shipped with this flow. |
0 commit comments