From 549413b59e10d2f34e2dbe0f1f0e2816f6dee43b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 5 May 2026 15:22:28 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20replace=20O(n=C2=B2)=20linear=20scans=20?= =?UTF-8?q?with=20O(1)=20Map=20lookups=20in=20getUserActivityData?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getUserActivityData() was calling .find() on four arrays and .filter() on a fifth inside a .map() over up to 500 users, producing O(users × entries) CPU usage that scaled catastrophically. Pre-index each result array into a Map keyed by userId so every per-user lookup is O(1). https://claude.ai/code/session_01R9UxmCEUgGCK58iJpMhuBY --- src/lib/api/analytics.ts | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/lib/api/analytics.ts b/src/lib/api/analytics.ts index 11fcb52..8fe6f7b 100644 --- a/src/lib/api/analytics.ts +++ b/src/lib/api/analytics.ts @@ -347,21 +347,30 @@ export async function getUserActivityData(): Promise> { .where(ne(deck.userId, 'system')) .groupBy(deck.userId, deck.id, deck.name); + // Index all arrays by userId for O(1) lookups instead of O(n) per user + const deckCountMap = new Map(deckCounts.map((d) => [d.userId, d])); + const cardCountMap = new Map(cardCounts.map((c) => [c.userId, c])); + const sharesSentMap = new Map(sharesSent.map((s) => [s.userId, s])); + const sharesReceivedMap = new Map(sharesReceived.map((r) => [r.userId, r])); + const userDecksMap = new Map(); + for (const ud of userDecksWithCards) { + if (!userDecksMap.has(ud.userId)) userDecksMap.set(ud.userId, []); + userDecksMap.get(ud.userId)!.push(ud); + } + // Build user activity data const userActivity: Array = allUsers.map((user) => { - const deckData = deckCounts.find((d) => d.userId === user.id); - const cardData = cardCounts.find((c) => c.userId === user.id); - const sentData = sharesSent.find((s) => s.userId === user.id); - const receivedData = sharesReceived.find((r) => r.userId === user.id); + const deckData = deckCountMap.get(user.id); + const cardData = cardCountMap.get(user.id); + const sentData = sharesSentMap.get(user.id); + const receivedData = sharesReceivedMap.get(user.id); // Get user's decks - const userDecks = userDecksWithCards - .filter((ud) => ud.userId === user.id) - .map((ud) => ({ - id: ud.deckId, - name: ud.deckName, - cardCount: ud.cardCount, - })); + const userDecks = (userDecksMap.get(user.id) ?? []).map((ud) => ({ + id: ud.deckId, + name: ud.deckName, + cardCount: ud.cardCount, + })); // Determine last activity let lastActivity: Date | null = null;