From 9231487aa994e83bcba68204b33aaab4d0c2cc9f Mon Sep 17 00:00:00 2001 From: Daniel Xu Date: Sun, 31 May 2026 18:50:59 -0700 Subject: [PATCH] Speed up dashboard navigation --- .../components/route-navigation-loader.svelte | 23 ++- src/lib/server/db/schema.ts | 188 ++++++++++-------- src/lib/server/runs.ts | 94 +++++---- src/routes/(app)/runs/+page.server.ts | 30 ++- 4 files changed, 210 insertions(+), 125 deletions(-) diff --git a/src/lib/components/route-navigation-loader.svelte b/src/lib/components/route-navigation-loader.svelte index 42cf336..dc42523 100644 --- a/src/lib/components/route-navigation-loader.svelte +++ b/src/lib/components/route-navigation-loader.svelte @@ -4,6 +4,8 @@ import DotMatrixLoader from '$lib/components/dotmatrix-loader.svelte'; let { scope }: { scope: 'app' | 'root' } = $props(); + let visible = $state(false); + let timer: ReturnType | undefined; function isAppPath(pathname: string | undefined) { if (!pathname) return false; @@ -19,9 +21,28 @@ if (scope === 'app') return fromApp && toApp; return !fromApp || !toApp; }); + + $effect(() => { + if (timer) clearTimeout(timer); + if (!active) { + visible = false; + return; + } + + timer = setTimeout( + () => { + visible = true; + }, + scope === 'root' ? 180 : 120 + ); + + return () => { + if (timer) clearTimeout(timer); + }; + }); -{#if active} +{#if visible} {#if scope === 'app'}
[ + index('runs_owner_started_at_idx').on(table.ownerUserId, table.startedAt.desc()), + index('runs_owner_status_started_at_idx').on( + table.ownerUserId, + table.status, + table.startedAt.desc() + ) + ] +); -export const providerCredentials = pgTable('provider_credentials', { - id: serial('id').primaryKey(), - publicId: text('public_id').notNull().unique(), - ownerUserId: text('owner_user_id').notNull(), - provider: providerEnum('provider').notNull(), - authType: credentialAuthTypeEnum('auth_type').notNull().default('api_key'), - label: text('label').notNull(), - encryptedApiKey: text('encrypted_api_key'), - encryptedOAuthSession: text('encrypted_oauth_session'), - accountEmail: text('account_email'), - keyPreview: text('key_preview'), - browserbaseProjectId: text('browserbase_project_id'), - isEnabled: boolean('is_enabled').notNull().default(true), - createdAt: timestamp('created_at') - .notNull() - .default(sql`CURRENT_TIMESTAMP`), - updatedAt: timestamp('updated_at') - .notNull() - .default(sql`CURRENT_TIMESTAMP`) -}); +export const providerCredentials = pgTable( + 'provider_credentials', + { + id: serial('id').primaryKey(), + publicId: text('public_id').notNull().unique(), + ownerUserId: text('owner_user_id').notNull(), + provider: providerEnum('provider').notNull(), + authType: credentialAuthTypeEnum('auth_type').notNull().default('api_key'), + label: text('label').notNull(), + encryptedApiKey: text('encrypted_api_key'), + encryptedOAuthSession: text('encrypted_oauth_session'), + accountEmail: text('account_email'), + keyPreview: text('key_preview'), + browserbaseProjectId: text('browserbase_project_id'), + isEnabled: boolean('is_enabled').notNull().default(true), + createdAt: timestamp('created_at') + .notNull() + .default(sql`CURRENT_TIMESTAMP`), + updatedAt: timestamp('updated_at') + .notNull() + .default(sql`CURRENT_TIMESTAMP`) + }, + (table) => [index('provider_credentials_owner_idx').on(table.ownerUserId)] +); export const oauthConnectStates = pgTable( 'oauth_connect_states', @@ -242,7 +257,10 @@ export const events = pgTable( .notNull() .default(sql`CURRENT_TIMESTAMP`) }, - (table) => [uniqueIndex('events_run_sequence_idx').on(table.runId, table.sequence)] + (table) => [ + index('events_run_id_idx').on(table.runId), + uniqueIndex('events_run_sequence_idx').on(table.runId, table.sequence) + ] ); export const artifacts = pgTable('artifacts', { @@ -263,44 +281,52 @@ export const artifacts = pgTable('artifacts', { .default(sql`CURRENT_TIMESTAMP`) }); -export const evaluations = pgTable('evaluations', { - id: serial('id').primaryKey(), - publicId: text('public_id').notNull().unique(), - runId: integer('run_id') - .notNull() - .references(() => runs.id, { onDelete: 'cascade' }), - status: evaluationStatusEnum('status').notNull().default('pending'), - goalCompleted: boolean('goal_completed'), - violatedConstraints: boolean('violated_constraints'), - repeatedActions: boolean('repeated_actions'), - neededHumanApproval: boolean('needed_human_approval'), - score: integer('score'), - summary: text('summary'), - explanation: text('explanation'), - data: jsonb('data'), - createdAt: timestamp('created_at') - .notNull() - .default(sql`CURRENT_TIMESTAMP`), - completedAt: timestamp('completed_at') -}); +export const evaluations = pgTable( + 'evaluations', + { + id: serial('id').primaryKey(), + publicId: text('public_id').notNull().unique(), + runId: integer('run_id') + .notNull() + .references(() => runs.id, { onDelete: 'cascade' }), + status: evaluationStatusEnum('status').notNull().default('pending'), + goalCompleted: boolean('goal_completed'), + violatedConstraints: boolean('violated_constraints'), + repeatedActions: boolean('repeated_actions'), + neededHumanApproval: boolean('needed_human_approval'), + score: integer('score'), + summary: text('summary'), + explanation: text('explanation'), + data: jsonb('data'), + createdAt: timestamp('created_at') + .notNull() + .default(sql`CURRENT_TIMESTAMP`), + completedAt: timestamp('completed_at') + }, + (table) => [index('evaluations_run_created_at_idx').on(table.runId, table.createdAt.desc())] +); -export const evaluationFindings = pgTable('evaluation_findings', { - id: serial('id').primaryKey(), - evaluationId: integer('evaluation_id') - .notNull() - .references(() => evaluations.id, { onDelete: 'cascade' }), - runId: integer('run_id') - .notNull() - .references(() => runs.id, { onDelete: 'cascade' }), - severity: findingSeverityEnum('severity').notNull(), - category: findingCategoryEnum('category').notNull(), - message: text('message').notNull(), - eventId: integer('event_id').references(() => events.id, { onDelete: 'set null' }), - data: jsonb('data'), - createdAt: timestamp('created_at') - .notNull() - .default(sql`CURRENT_TIMESTAMP`) -}); +export const evaluationFindings = pgTable( + 'evaluation_findings', + { + id: serial('id').primaryKey(), + evaluationId: integer('evaluation_id') + .notNull() + .references(() => evaluations.id, { onDelete: 'cascade' }), + runId: integer('run_id') + .notNull() + .references(() => runs.id, { onDelete: 'cascade' }), + severity: findingSeverityEnum('severity').notNull(), + category: findingCategoryEnum('category').notNull(), + message: text('message').notNull(), + eventId: integer('event_id').references(() => events.id, { onDelete: 'set null' }), + data: jsonb('data'), + createdAt: timestamp('created_at') + .notNull() + .default(sql`CURRENT_TIMESTAMP`) + }, + (table) => [index('evaluation_findings_run_severity_idx').on(table.runId, table.severity)] +); export const regressionRunStatusEnum = pgEnum('regression_run_status', [ 'pending', diff --git a/src/lib/server/runs.ts b/src/lib/server/runs.ts index b40dcb3..b1e29ba 100644 --- a/src/lib/server/runs.ts +++ b/src/lib/server/runs.ts @@ -11,6 +11,15 @@ import { publicId } from '$lib/server/public-id'; type RunStatus = (typeof runStatusEnum.enumValues)[number]; +function nowMs() { + return performance.now(); +} + +function logTiming(label: string, startedAt: number) { + if (process.env.NODE_ENV === 'production') return; + console.info(`[runs] ${label} ${Math.round(performance.now() - startedAt)}ms`); +} + export async function createRun(input: { ownerUserId: string; schemaVersion?: string; @@ -118,6 +127,7 @@ export async function listRuns(input: { limit: number; offset: number; }) { + const startedAt = nowMs(); const filters = [ eq(runs.ownerUserId, input.ownerUserId), input.status ? eq(runs.status, input.status) : undefined, @@ -131,61 +141,68 @@ export async function listRuns(input: { ].filter(Boolean); const where = filters.length ? and(...filters) : undefined; - const rows = await db - .select({ - id: runs.publicId, - name: runs.name, - goal: runs.goal, - status: runs.status, - agentName: runs.agentName, - agentVersion: runs.agentVersion, - environment: runs.environment, - startedAt: runs.startedAt, - endedAt: runs.endedAt, - eventCount: sql`count(distinct ${events.id})`, - latestEvaluationScore: sql`( + const [rows, [{ total }]] = await Promise.all([ + db + .select({ + id: runs.publicId, + name: runs.name, + goal: runs.goal, + status: runs.status, + agentName: runs.agentName, + agentVersion: runs.agentVersion, + environment: runs.environment, + startedAt: runs.startedAt, + endedAt: runs.endedAt, + eventCount: sql`( + select count(*) + from ${events} + where ${events.runId} = ${runs.id} + )`, + latestEvaluationScore: sql`( select ${evaluations.score} from ${evaluations} where ${evaluations.runId} = ${runs.id} order by ${evaluations.createdAt} desc limit 1 )`, - latestEvaluationStatus: sql`( + latestEvaluationStatus: sql`( select ${evaluations.status} from ${evaluations} where ${evaluations.runId} = ${runs.id} order by ${evaluations.createdAt} desc limit 1 )` - }) - .from(runs) - .leftJoin(events, eq(events.runId, runs.id)) - .where(where) - .groupBy(runs.id) - .orderBy(desc(runs.startedAt)) - .limit(input.limit) - .offset(input.offset); - - const [{ total }] = await db.select({ total: count() }).from(runs).where(where); + }) + .from(runs) + .where(where) + .orderBy(desc(runs.startedAt)) + .limit(input.limit) + .offset(input.offset), + db.select({ total: count() }).from(runs).where(where) + ]); + logTiming('listRuns', startedAt); return { runs: rows, total }; } export async function getRunDashboardMetricsForUser(ownerUserId: string) { - const rows = await db - .select({ status: runs.status, total: count() }) - .from(runs) - .where(eq(runs.ownerUserId, ownerUserId)) - .groupBy(runs.status); - const warningRows = await db - .select({ total: count() }) - .from(evaluationFindings) - .innerJoin(runs, eq(evaluationFindings.runId, runs.id)) - .where( - and( - eq(runs.ownerUserId, ownerUserId), - or(eq(evaluationFindings.severity, 'warning'), eq(evaluationFindings.severity, 'error')) + const startedAt = nowMs(); + const [rows, warningRows] = await Promise.all([ + db + .select({ status: runs.status, total: count() }) + .from(runs) + .where(eq(runs.ownerUserId, ownerUserId)) + .groupBy(runs.status), + db + .select({ total: count() }) + .from(evaluationFindings) + .innerJoin(runs, eq(evaluationFindings.runId, runs.id)) + .where( + and( + eq(runs.ownerUserId, ownerUserId), + or(eq(evaluationFindings.severity, 'warning'), eq(evaluationFindings.severity, 'error')) + ) ) - ); + ]); const metrics = { running: 0, @@ -197,5 +214,6 @@ export async function getRunDashboardMetricsForUser(ownerUserId: string) { for (const row of rows) metrics[row.status] = row.total; const completed = metrics.success + metrics.failed + metrics.cancelled; const successRate = completed ? Math.round((metrics.success / completed) * 100) : 0; + logTiming('getRunDashboardMetricsForUser', startedAt); return { ...metrics, successRate }; } diff --git a/src/routes/(app)/runs/+page.server.ts b/src/routes/(app)/runs/+page.server.ts index 1f9e0c5..dcfab33 100644 --- a/src/routes/(app)/runs/+page.server.ts +++ b/src/routes/(app)/runs/+page.server.ts @@ -1,4 +1,5 @@ import { requireUserId } from '$lib/server/auth'; +import { dev } from '$app/environment'; import { getRunDashboardMetricsForUser, listRuns } from '$lib/server/runs'; import { readOpenAIOAuthConfig, shouldUseDeviceAuth } from '$lib/server/openai-oauth'; import { @@ -8,18 +9,37 @@ import { } from '$lib/server/provider-credentials'; import { runStatusSchema } from '$lib/server/validation'; +function nowMs() { + return performance.now(); +} + +async function timed(label: string, work: Promise) { + const startedAt = nowMs(); + try { + return await work; + } finally { + if (dev) { + console.info(`[runs.load] ${label} ${Math.round(performance.now() - startedAt)}ms`); + } + } +} + export async function load(event) { + const startedAt = nowMs(); const userId = requireUserId(event); const { url } = event; const statusParam = url.searchParams.get('status'); const status = statusParam ? runStatusSchema.parse(statusParam) : undefined; const q = url.searchParams.get('q') ?? undefined; - const [result, metrics] = await Promise.all([ - listRuns({ ownerUserId: userId, status, q, limit: 50, offset: 0 }), - getRunDashboardMetricsForUser(userId) + const [result, metrics, credentials, openaiOAuthConfig] = await Promise.all([ + timed('listRuns', listRuns({ ownerUserId: userId, status, q, limit: 50, offset: 0 })), + timed('metrics', getRunDashboardMetricsForUser(userId)), + timed('credentials', listProviderCredentials(userId)), + timed('openaiOAuthConfig', Promise.resolve(readOpenAIOAuthConfig(url.origin))) ]); - const credentials = await listProviderCredentials(userId); - const openaiOAuthConfig = readOpenAIOAuthConfig(url.origin); + if (dev) { + console.info(`[runs.load] total ${Math.round(performance.now() - startedAt)}ms`); + } return { ...result, metrics,