Skip to content

Optimize analytics queries and add database indexing#8

Merged
aikenahac merged 1 commit into
masterfrom
claude/fix-cpu-usage-MXNio
May 8, 2026
Merged

Optimize analytics queries and add database indexing#8
aikenahac merged 1 commit into
masterfrom
claude/fix-cpu-usage-MXNio

Conversation

@aikenahac
Copy link
Copy Markdown
Owner

@aikenahac aikenahac commented May 6, 2026

Summary

This PR improves application performance through database query optimization, caching, and strategic indexing. It refactors analytics functions to use efficient aggregated queries and implements Next.js caching for frequently accessed data.

Key Changes

Database Performance

  • Added strategic indexes on frequently queried columns:

    • Deck.user_id index for user-specific deck lookups
    • Card.deck_id and Card.user_id indexes for card filtering
    • Composite Card(type, deck_id) index for card type queries
    • DeckShare indexes on deck_id, shared_with_user_id, and shared_by_user_id
  • Optimized analytics queries in src/lib/api/analytics.ts:

    • Consolidated multiple separate COUNT queries into single aggregated queries using PostgreSQL's FILTER clause
    • Reduced getSystemStats() from 8 database queries to 2 queries
    • Eliminated redundant joins and filtering operations

Caching & Revalidation

  • Implemented Next.js unstable_cache for all analytics functions with 5-minute revalidation:
    • getSystemStats()
    • getUserGrowthByMonth()
    • getDeckCreationTrends()
    • getCardCreationTrends()
    • getSharingActivityTrends()
    • getUserActivityData()
  • Added revalidate = 300 to analytics admin page for consistent cache behavior

API Optimization

  • Batched Clerk API calls in deck page (src/app/(app)/decks/[id]/page.tsx):
    • Changed from sequential getUser() calls to single getUserList() batch call
    • Reduces API calls from N to 1 for N shared users

Infrastructure

  • Increased database connection pool size from 10 to 20 for better concurrency handling

Implementation Details

  • Analytics functions now use private _functionName() implementations wrapped with unstable_cache() for cleaner separation of concerns
  • All cached functions use consistent tagging strategy (analytics tag) for potential future cache invalidation
  • Database migration (0008) creates all indexes with proper naming conventions matching Drizzle ORM patterns

…hing Clerk

- Add btree indexes on Deck.user_id, Card.deck_id, Card.user_id,
  Card(type, deck_id), DeckShare.shared_by_user_id. Every deck list,
  deck view, and analytics aggregation was doing sequential scans
  because only DeckShare had indexes; this is the primary CPU sink.
- Wrap all six analytics functions in unstable_cache (5 min TTL,
  'analytics' tag) and add `revalidate = 300` to the analytics page
  so each visit no longer fans out 1500+ Clerk calls and 10+ COUNTs.
- Collapse getSystemStats from 10 sequential COUNT queries into 2
  using COUNT(*) FILTER (WHERE …) aggregates over Deck and Card.
- Replace per-share Clerk getUser loop on the deck detail page with
  a single batched users.getUserList({ userId }) call.
- Bump pg pool max from 10 to 20 so analytics' parallel queries
  don't starve regular request handling.

https://claude.ai/code/session_01R9UxmCEUgGCK58iJpMhuBY
@aikenahac aikenahac merged commit 153ffb6 into master May 8, 2026
2 of 3 checks passed
@aikenahac aikenahac deleted the claude/fix-cpu-usage-MXNio branch May 8, 2026 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants