Stop the whole dashboard waiting on one query before anything can fetch - #30
Merged
Merged
Conversation
Measured on production 2026-07-31: the project keywords page fired 14 server functions in THREE sequential waves (starting ~1.7s, ~5.7s, ~11.5s) and finished at 15.8s. The waves are what cost, because a server-function call against this Worker carries ~4s of FIXED per-invocation overhead. That figure is not speculation: `/api/does-not-exist` -- a 404 that runs no handler, touches no database and does no auth -- takes 4.16s, while a static asset on the same host takes 0.07s. Ten back-to-back calls were 3.4-5.7s with not one fast, so there is no isolate reuse to wait for. The dashboard put every card behind `projectsQuery.isPending`. Since that query costs ~4s, no card could BEGIN fetching until 4s in, turning independent queries into a second wave. `projectId` comes from the route params, not from that query. Seven of the nine cards are keyed only on it, and each already renders its own skeleton and degrades independently -- which is why removing the page-level gate leaves no holes. They now start immediately. The two that genuinely need the project's `domain` (AnalyzeProjectCard, BacklinksCard) still wait, and are rendered in place behind a skeleton so the layout does not reflow when they arrive. `DashboardLoadingState` goes with it: the header now carries its own two-line skeleton and every card brings its own, so the page-level loader had no remaining caller. knip caught it. This does NOT fix the 4s. It removes one of the three waves that multiply it. The 4s itself was investigated on 2026-07-29 and the obvious fix was FALSIFIED -- cutting the startup chunk 30% (5,933 -> 4,166 KB) moved it not at all, so it is not parse time. Do not attack it with more bundle shrinking. ci:check clean, 2,129 tests passing. The wall-clock claim needs a production re-measure after deploy; that is the next step, not an assumption. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
flyrocketseo | c72bdc0 | Jul 31 2026, 07:16 AM |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First fix from a production performance measurement. It removes one of three
waves, not the per-call cost.
What I measured on prod (2026-07-31, signed in)
/favicon.ico— static asset, Worker never runs/api/does-not-exist— 404, no handler, no DB, no auth/api/auth/get-sessionA 404 costs the same as a real request, so ~4s is fixed per-invocation overhead
before any of our code runs. Ten back-to-back calls: 3.4 / 4.0 / 5.7 / 5.1 / 3.9
/ 4.2 / 4.6 / 4.6 / 3.4 / 3.8s — not one fast, so there's no isolate reuse to
wait for.
The page fires 14 server functions in three sequential waves (~1.7s, ~5.7s,
~11.5s). The waves are what multiply the 4s into 16s.
The change
The dashboard put every card behind
projectsQuery.isPending. Since that querycosts ~4s, no card could begin fetching until 4s in — turning independent
queries into a second wave.
projectIdcomes from the route params, not from that query. Seven of the ninecards are keyed only on it, and each already renders its own skeleton and
degrades independently — which is why removing the page-level gate leaves no
holes.
The two that genuinely need
domain(AnalyzeProjectCard,BacklinksCard)still wait, rendered in place behind a skeleton so the layout doesn't reflow.
DashboardLoadingStategoes with it — the header now has its own skeleton andevery card brings its own, so it had no caller left. knip caught that.
What this does not do
It does not fix the 4s. And the obvious fix for that was already tested and
falsified on 2026-07-29: cutting the startup chunk 30% (5,933 → 4,166 KB)
moved it not at all. It isn't parse time — the remaining suspect is top-level
module evaluation (
agents,think,ai,better-auth,autumnallinitialise at import). Please don't attack it with more bundle shrinking.
On Supabase, since it prompted this
It would fix none of this. The
sessiontable has 2 rows with a uniqueindex on
token; a 404 touches no database and costs the same 4s. Anindependent Codex pass agreed and added a detail I'd missed: the existing
Postgres path uses Hyperdrive with
max: 1, soPromise.allsections wouldqueue on one connection — potentially making parallel work slower, plus
regional latency.
Verification
pnpm ci:checkclean, 2,129 tests passing.The wall-clock win is not yet proven — it needs a production re-measure after
deploy, using exactly the numbers above. That's the next step, not an assumption.
🤖 Generated with Claude Code