Skip to main content
← Back to list
01Issue
BugShippedSwamp ClubPublic
Assigneeskeeb

Relationships

#1503 Profile score section blocks the render on a ClickHouse read

Opened by keeb · 8/2/2026· Shipped 8/14/2026

Split out of #1496, which fixed the leaderboard but deliberately left this.

What

routes/u/[username].tsx:168-173 awaits loadProfileScore inside its Promise.all, so the profile page's render is gated on a ClickHouse read. The leaderboard no longer works this way — #1496 made it render its frame and fill the boards in — and the profile is now the remaining surface where a slow ledger holds a page.

Everything else on the page (bio, badges, collectives, lab, extensions) is ready without it.

Why it was deferred rather than folded in

The benefit is unmeasured, and that is the actual work here. The score reads sit in a Promise.all alongside every other section, so deferring them only speeds the page if the score read is the long pole. If some Mongo read dominates that race, frame-first on the score section buys resilience but not speed — and the plan should say which.

So step one is to measure which read dominates routes/u/[username].tsx:168, not to start moving code.

Notes

  • routes/api/v1/users/[username]/score-history.ts exists; check whether it already returns the headline and breakdown the section needs or only the history series. That is the difference between wiring an existing endpoint and designing one.
  • The combat log on the same page is already a separate lazy request, so frame-first here would make the score section consistent with its neighbour rather than introducing a new pattern.
  • The pattern and its traps (one request, pending-is-not-empty, the asOf pin) are written up in CLAUDE.md under the Fresh section.
  • #1496 already removed the symptom this would have masked: board pressure can no longer blank the profile score, because the breakers are now split per read class.
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 11 MOREREVIEW+ 3 MOREPR_MERGED+ 1 MORENOTIFICATION_SKIPPED

Shipped

8/14/2026, 8:16:01 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
keeb assigned keeb8/14/2026, 6:05:27 PM
Editable. Press Enter to edit.

keeb commented 8/10/2026, 7:22:08 PM

Cross-link: related to epic #1572, and specifically complementary to #1576.

This issue asks for a measurement first — "which read dominates routes/u/[username].tsx" — rather than moving code. Partial answer from the 2026-08-10 audit, on the ClickHouse side:

loadProfileScore -> dailyHistory issues two queries. Both are nominally "keyed", but candidateIds builds its candidate set with WHERE username = {u} against score_daily and score_grants, and username is in neither table's sort key. Both arms are full scans, and the CTE is inlined three times. Measured on a 10B-scale synthetic: 42.88M rows / 107 MiB for one single-user profile read. Filed as #1576.

So on the ClickHouse side the read is far more expensive than the module docs claim (~8ms / 4.4 MiB), and gets worse with total platform events rather than with the operative's own data.

What this does not answer is the actual question here, which is whether the CH read is the long pole relative to the Mongo reads in the same Promise.all. It probably is not, at current scale: the profile does ~15 Mongo round trips with several serial hops (findByUsername alone is 2 serial finds, isGenesisPublic is 2 serial flag reads on a cold cache, and collective membership is read 3x on an own-profile render). That is the still-unshipped 2026-07 audit territory (#921-#926).

Suggested framing: #1576 fixes the CH read's cost; this issue should stay about when it happens, and its measurement should compare against the Mongo side rather than the CH side alone.

Sign in to post a ripple.