perf(practice): eliminate redundant getViewerId and user lookups
Refactor getPlayersWithSkillData() to return viewerId and userId along with players, avoiding 2 redundant calls in Practice page: - Previous: 3 calls to getViewer() (via getViewerId) + 2 user lookups - Now: 1 call to getViewer() + 1 user lookup This should reduce Practice page SSR time by eliminating duplicate auth checks and database queries. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
dbc45b97b0
commit
1914bcf9d0
|
|
@ -8,8 +8,8 @@ import { getPlayersWithSkillData } from '@/lib/curriculum/server'
|
|||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
const players = await getPlayersWithSkillData()
|
||||
return NextResponse.json({ players })
|
||||
const result = await getPlayersWithSkillData()
|
||||
return NextResponse.json({ players: result.players })
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch players with skill data:', error)
|
||||
return NextResponse.json({ error: 'Failed to fetch players' }, { status: 500 })
|
||||
|
|
|
|||
|
|
@ -1,25 +1,6 @@
|
|||
import { eq } from 'drizzle-orm'
|
||||
import { db, schema } from '@/db'
|
||||
import { getPlayersWithSkillData } from '@/lib/curriculum/server'
|
||||
import { getViewerId } from '@/lib/viewer'
|
||||
import { PracticeClient } from './PracticeClient'
|
||||
|
||||
/**
|
||||
* Get or create user record for a viewerId (guestId)
|
||||
*/
|
||||
async function getOrCreateUser(viewerId: string) {
|
||||
let user = await db.query.users.findFirst({
|
||||
where: eq(schema.users.guestId, viewerId),
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
const [newUser] = await db.insert(schema.users).values({ guestId: viewerId }).returning()
|
||||
user = newUser
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
/**
|
||||
* Practice page - Server Component
|
||||
*
|
||||
|
|
@ -30,13 +11,8 @@ async function getOrCreateUser(viewerId: string) {
|
|||
*/
|
||||
export default async function PracticePage() {
|
||||
// Fetch players with skill data directly on server - no HTTP round-trip
|
||||
const players = await getPlayersWithSkillData()
|
||||
// Returns players, viewerId, and userId in a single call to avoid redundant lookups
|
||||
const { players, viewerId, userId } = await getPlayersWithSkillData()
|
||||
|
||||
// Get viewer ID for session observation
|
||||
const viewerId = await getViewerId()
|
||||
|
||||
// Get database user ID for parent socket notifications
|
||||
const user = await getOrCreateUser(viewerId)
|
||||
|
||||
return <PracticeClient initialPlayers={players} viewerId={viewerId} userId={user.id} />
|
||||
return <PracticeClient initialPlayers={players} viewerId={viewerId} userId={userId} />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,15 @@ export async function getPlayersForViewer(): Promise<Player[]> {
|
|||
return players
|
||||
}
|
||||
|
||||
/**
|
||||
* Result from getPlayersWithSkillData including context for page rendering
|
||||
*/
|
||||
export interface PlayersWithSkillDataResult {
|
||||
players: StudentWithSkillData[]
|
||||
viewerId: string
|
||||
userId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all players for the current viewer with enhanced skill data.
|
||||
*
|
||||
|
|
@ -97,11 +106,13 @@ export async function getPlayersForViewer(): Promise<Player[]> {
|
|||
* - skillCategory: Computed highest-level skill category
|
||||
* - intervention: Intervention data if student needs attention
|
||||
*
|
||||
* Also returns viewerId and userId to avoid redundant calls in page components.
|
||||
*
|
||||
* Performance: Uses batched queries to avoid N+1 query patterns.
|
||||
* - Single query for all skill mastery records across all players
|
||||
* - Single query for session history across all players needing intervention
|
||||
* - Returns viewerId/userId to avoid redundant getViewerId() calls
|
||||
*/
|
||||
export async function getPlayersWithSkillData(): Promise<StudentWithSkillData[]> {
|
||||
export async function getPlayersWithSkillData(): Promise<PlayersWithSkillDataResult> {
|
||||
const viewerId = await getViewerId()
|
||||
|
||||
// Get or create user record
|
||||
|
|
@ -114,6 +125,8 @@ export async function getPlayersWithSkillData(): Promise<StudentWithSkillData[]>
|
|||
user = newUser
|
||||
}
|
||||
|
||||
const userId = user.id
|
||||
|
||||
// Get player IDs linked via parent_child table
|
||||
const linkedPlayerIds = await db.query.parentChild.findMany({
|
||||
where: eq(parentChild.parentUserId, user.id),
|
||||
|
|
@ -135,7 +148,7 @@ export async function getPlayersWithSkillData(): Promise<StudentWithSkillData[]>
|
|||
}
|
||||
|
||||
if (players.length === 0) {
|
||||
return []
|
||||
return { players: [], viewerId, userId }
|
||||
}
|
||||
|
||||
// OPTIMIZATION: Batch query all skill mastery records for all players at once
|
||||
|
|
@ -186,7 +199,7 @@ export async function getPlayersWithSkillData(): Promise<StudentWithSkillData[]>
|
|||
// Intervention badges are helpful but not critical for initial render.
|
||||
// They can be computed lazily on the client if needed.
|
||||
// This avoids N additional database queries for session history.
|
||||
return playersWithBasicSkills
|
||||
return { players: playersWithBasicSkills, viewerId, userId }
|
||||
}
|
||||
|
||||
// Re-export the individual functions for granular prefetching
|
||||
|
|
|
|||
Loading…
Reference in New Issue