Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit28b52be

perf(repo-home): stop re-fetching the same owner/repo rows on every load

perf(repo-home): stop re-fetching the same owner/repo rows on every load

The repo home page issued ~9 sequential round-trips to Neon, and four of
them re-read rows it already had:

  assertRepoReadable  users            <- lookup 1
  assertRepoReadable  repositories     <- lookup 2
  resolveRepoAccess   repositories
  starInfo            users            <- same row as lookup 1
  starInfo            repositories     <- same row as lookup 2
  starInfo            stars
  countPendingForRepo ...
  onboarding          repositories     <- same row as lookup 2, again
  onboarding          repoOnboardingData

Every one of those is a serial network hop to managed Postgres, which is
why an owner-authenticated repo home measured 7.8s against 2.1s for the
same page anonymous — the gap scaled with page complexity, not with auth.

- Collapse assertRepoReadable's users + repositories lookups into a single
  innerJoin, and select the full repo row rather than three columns (same
  round trip, strictly more data).
- Publish the resolved {ownerRow, repoRow, access} on the request context
  as `resolvedRepoCtx` (declared on AuthEnv), so downstream blocks reuse
  what the access gate already fetched.
- starInfo and the onboarding gate now read that instead of re-querying.

Net for a populated repo: 9 sequential lookups -> 4. The signature of
assertRepoReadable is unchanged, so all 12 call sites are untouched and
the other 11 pick up the join for free.

Typecheck clean. Test suite: 3122 pass / 4 fail — the same 4 that already
fail on main, unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ccantynz-alt committed on July 27, 2026Parent: 2f1a07d
2 files changed+415028b52be310269a33b991ec6a44f0a6d7ce0e59f7
2 changed files+41−50
Modifiedsrc/middleware/auth.ts+11−1View fileUnifiedSplit
1212import { eq, gt } from "drizzle-orm";
1313import { db } from "../db";
1414import { sessions, users, oauthAccessTokens, apiTokens } from "../db/schema";
15import type { User } from "../db/schema";
15import type { User, Repository } from "../db/schema";
1616import { sessionCache } from "../lib/cache";
1717import { sha256Hex } from "../lib/oauth";
1818
3030 requestStart?: number;
3131 /** Set by `requireRepoAccess` middleware — the resolved repository row. */
3232 repository?: unknown;
33 /**
34 * Set by web.tsx's `assertRepoReadable` gate — the owner + repository rows
35 * it already fetched (and access-checked) for this request, so downstream
36 * handlers can reuse them instead of re-issuing the same lookups.
37 */
38 resolvedRepoCtx?: {
39 ownerRow: User;
40 repoRow: Repository;
41 access: string;
42 };
3343 /** Set by `requireRepoAccess` — the caller's access level ('read'|'write'|'admin'|'owner'). */
3444 repoAccess?: string;
3545 /** Set by API-auth middleware — how the caller authenticated. */
Modifiedsrc/routes/web.tsx+30−49View fileUnifiedSplit
136136 // Case-insensitive slug match; carry back the CANONICAL owner/repo so we
137137 // can (a) never feed the caller's raw casing to the git layer and (b)
138138 // redirect to the canonical URL below.
139 const [ownerRow] = await db
140 .select({ id: users.id, username: users.username })
141 .from(users)
142 .where(sql`lower(${users.username}) = lower(${owner})`)
143 .limit(1);
144 if (!ownerRow) return notFound();
145
146 const [repoRow] = await db
147 .select({
148 id: repositories.id,
149 name: repositories.name,
150 isPrivate: repositories.isPrivate,
151 })
139 // Single joined lookup. This used to be two sequential round-trips
140 // (users, then repositories); on a managed Postgres every hop is real
141 // latency and the repo-home handler re-fetched these same two rows again
142 // further down. Fetch the FULL repo row once and publish it on the
143 // context (below) so downstream blocks can reuse it instead of re-querying.
144 const [joined] = await db
145 .select({ owner: users, repo: repositories })
152146 .from(repositories)
147 .innerJoin(users, eq(repositories.ownerId, users.id))
153148 .where(
154149 and(
155 eq(repositories.ownerId, ownerRow.id),
150 sql`lower(${users.username}) = lower(${owner})`,
156151 sql`lower(${repositories.name}) = lower(${repo})`
157152 )
158153 )
159154 .limit(1);
160 if (!repoRow) return notFound();
155 if (!joined) return notFound();
156 const ownerRow = joined.owner;
157 const repoRow = joined.repo;
161158
162159 const access = await resolveRepoAccess({
163160 repoId: repoRow.id,
183180 return c.redirect(segs.join("/") + search, 302);
184181 }
185182
183 // Publish the already-resolved rows for this request. Handlers that need
184 // the owner/repo row can read this instead of issuing their own lookups —
185 // repo-home alone was re-fetching `users` once and `repositories` twice
186 // more, all sequentially, for rows we already had in hand.
187 c.set("resolvedRepoCtx", { ownerRow, repoRow, access });
188
186189 return null;
187190 } catch (err) {
188191 // Fail CLOSED. A DB is configured (checked above) but the privacy-flag
29942997 // Star info fetched in parallel with tree
29952998 (async () => {
29962999 try {
2997 const [ownerUser] = await db
2998 .select()
2999 .from(users)
3000 .where(eq(users.username, owner))
3001 .limit(1);
3002 if (!ownerUser)
3003 return {
3004 starCount: 0,
3005 starred: false,
3006 archived: false,
3007 isTemplate: false,
3008 forkCount: 0,
3009 description: null as string | null,
3010 pushedAt: null as Date | null,
3011 createdAt: null as Date | null,
3012 repoId: null as string | null,
3013 repoOwnerId: null as string | null,
3014 };
3015 const [repoRow] = await db
3016 .select()
3017 .from(repositories)
3018 .where(
3019 and(
3020 eq(repositories.ownerId, ownerUser.id),
3021 eq(repositories.name, repo)
3022 )
3023 )
3024 .limit(1);
3000 // assertRepoReadable already resolved (and access-checked) both rows
3001 // for this request — reuse them instead of repeating the two lookups.
3002 const resolved = c.get("resolvedRepoCtx") as
3003 | { repoRow: typeof repositories.$inferSelect }
3004 | undefined;
3005 const repoRow = resolved?.repoRow;
30253006 if (!repoRow)
30263007 return {
30273008 starCount: 0,
31483129 const repoLooksEmpty = tree.length === 0 && branches.length === 0;
31493130 if (repoLooksEmpty && repoId && user && repoOwnerId && user.id === repoOwnerId) {
31503131 try {
3151 // Check if onboarding_shown is still false (not yet dismissed)
3152 const [repoRow2] = await db
3153 .select({ onboardingShown: repositories.onboardingShown })
3154 .from(repositories)
3155 .where(eq(repositories.id, repoId))
3156 .limit(1);
3132 // onboarding_shown comes from the row assertRepoReadable already
3133 // resolved — no need for a third lookup of the same repository.
3134 const resolvedForOnboarding = c.get("resolvedRepoCtx") as
3135 | { repoRow: typeof repositories.$inferSelect }
3136 | undefined;
3137 const repoRow2 = resolvedForOnboarding?.repoRow;
31573138 if (repoRow2 && !repoRow2.onboardingShown) {
31583139 const [obRow] = await db
31593140 .select()
31603141