perf(db): add quiz_attempts indexes + fix advanced HTML question#114
Conversation
• Quiz performance: - Eliminate N+1 queries in getQuizQuestions by batch-loading answers - Optimize submitQuizAttempt with inArray-based batch verification - Reduce quiz page load from 21 queries to 2 - Add seed to randomizeQuizQuestions for stable randomization - Handle empty answers array and validate answer–question matching • Points system refactor (ledger-only): - Migrate to ledger-based points model (point_transactions as source of truth) - Remove users.points column and db.transaction() usage (neon-http compatible) - Store full attempt score in quiz_attempts.pointsEarned - Award only delta vs previous best attempt - Compute total points via SUM(point_transactions.points) - Optimize previous best lookup using MAX(pointsEarned) • Quiz content & seeding: - Add seed scripts for HTML, CSS, Git, JavaScript, TypeScript quizzes - Introduce advanced and fundamentals quiz trackse • UI & logic fixes: - Add pointsAwarded prop to QuizResult - Show "+N points added" or "no points (result not improved)" - Fix Dashboard to rely on ledger total • Leaderboard & DB performance: - Update leaderboard queries to LEFT JOIN + GROUP BY + SUM - Add index for point_transactions (user_id) - Apply drizzle migrations and snapshots Benefits: - Faster quiz loads and lower DB pressure - Neon-compatible (no transactions)
• Add quiz_attempts_user_id_idx for Dashboard queries optimization • Add quiz_attempts_quiz_id_idx for quiz statistics queries • Generate migration 0013_warm_dexter_bennett.sql • Reduces full table scans, improves Neon CU-hours usage • Fix HTML advanced quiz h1-h6 SEO question for technical accuracy - Clarify HTML5 allows multiple h1 in sections (one recommended for SEO) Performance impact: Dashboard queries use index scan instead of full table scan
✅ Deploy Preview for develop-devlovers ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📝 WalkthroughWalkthroughThis PR adds two database indexes (userIdIdx and quizIdIdx) to the quiz_attempts table to optimize query filtering by user and quiz ID. It includes the TypeScript schema definition, SQL migration file, and corresponding metadata updates. Additionally, quiz content is updated to revise h1 SEO guidance, and the order_items schema is enhanced with variant selection fields. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @frontend/drizzle/meta/0013_snapshot.json:
- Around line 1371-1384: The snapshot shows order_items additions (columns
selected_size and selected_color and updated unique index
order_items_order_variant_uq) but the migration file
0013_warm_dexter_bennett.sql only contains quiz_attempts index DDL; reconcile by
either (A) adding the missing ALTER TABLE ... ADD COLUMN ... NOT NULL DEFAULT ''
statements and the ALTER INDEX/CREATE UNIQUE INDEX/update for
order_items_order_variant_uq into 0013_warm_dexter_bennett.sql (ensuring SQL
matches the snapshot column types/defaults and index definition), or (B) if
those order_items changes belong to a different migration/branch, regenerate or
revert frontend/drizzle/meta/0013_snapshot.json so it matches the actual
migrations (remove selected_size/selected_color and index changes), and run the
ripgrep checks suggested (search for selected_size/selected_color and ALTER
TABLE order_items) to confirm no stray DDL is missing or duplicated; update the
migration or snapshot accordingly and re-run schema generation/tests.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
frontend/db/schema/quiz.tsfrontend/drizzle/0013_warm_dexter_bennett.sqlfrontend/drizzle/meta/0013_snapshot.jsonfrontend/drizzle/meta/_journal.jsonjson/quizzes/html/advanced/html-advanced-quiz-part3.json
🔇 Additional comments (5)
json/quizzes/html/advanced/html-advanced-quiz-part3.json (1)
224-259: LGTM! Technically accurate SEO guidance update.The revised h1-h6 guidance correctly balances HTML5 specifications (which allow multiple h1 elements within sectioning elements) with SEO best practices (recommending one h1 per page). The updated explanations and answer options across all three locales are consistent and accurate.
frontend/drizzle/meta/_journal.json (1)
96-102: LGTM! Standard migration journal entry.The journal entry is correctly formatted and properly sequenced after migration 0012.
frontend/db/schema/quiz.ts (1)
152-153: Confirm necessity of single-column indexes given existing composite indexes.The quizAttempts table already has composite indexes with these columns as leading keys:
- Line 154:
userCompletedAtIdxon (userId, completedAt)- Line 158:
quizPercentageCompletedAtIdxon (quizId, percentage, completedAt)- Line 161:
quizIntegrityScoreIdxon (quizId, integrityScore)PostgreSQL B-tree indexes can efficiently handle queries filtering only on the leading column of a composite index. Therefore, queries filtering solely on
userIdorquizIdshould already benefit from the existing composite indexes without needing dedicated single-column indexes.While there are edge cases where single-column indexes may offer marginal benefits, they come with tradeoffs:
- Increased storage and memory footprint
- Additional maintenance overhead on INSERT/UPDATE operations
- Higher Neon CU consumption (counterproductive to PR goal of reducing CU-hours)
Please verify that actual query patterns require these dedicated indexes and that the performance gains outweigh the costs.
frontend/drizzle/meta/0013_snapshot.json (1)
576-605: Approve quiz_attempts indexes in snapshot.The snapshot correctly reflects the two new B-tree indexes on quiz_attempts (user_id and quiz_id) that match the migration SQL file and schema declarations.
Note: Please see separate comment regarding potential index redundancy with existing composite indexes.
frontend/drizzle/0013_warm_dexter_bennett.sql (1)
1-2: Verify these indexes aren't redundant with existing composite indexes before deploying.The quiz_attempts table already has composite indexes with these leading columns:
userCompletedAtIdxon (user_id, completed_at)quizPercentageCompletedAtIdxon (quiz_id, percentage, completed_at)quizIntegrityScoreIdxon (quiz_id, integrity_score)In PostgreSQL, B-tree composite indexes efficiently serve queries filtering only on the leading column. Queries like
getUserQuizStats()andgetUserQuizzesProgress()filter solely on user_id and should already avoid full table scans usinguserCompletedAtIdx. Similarly,getQuizLeaderboard()filters on quiz_id with integrity_score and can usequizIntegrityScoreIdx.Adding dedicated single-column indexes introduces storage overhead, insert/update maintenance cost, and increased Neon CU consumption—contrary to the PR's goal to reduce CU-hours. Verify with PostgreSQL query plans that these new indexes provide measurable benefit over the existing composite indexes.
| "selected_size": { | ||
| "name": "selected_size", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "default": "''" | ||
| }, | ||
| "selected_color": { | ||
| "name": "selected_color", | ||
| "type": "text", | ||
| "primaryKey": false, | ||
| "notNull": true, | ||
| "default": "''" | ||
| }, |
There was a problem hiding this comment.
Critical: Snapshot includes order_items schema changes not present in migration SQL.
This snapshot shows new columns in the order_items table:
selected_size(text, not null, default '')selected_color(text, not null, default '')
And an updated unique index order_items_order_variant_uq that includes these new columns (lines 1450-1476).
However, the migration file frontend/drizzle/0013_warm_dexter_bennett.sql contains ONLY the quiz_attempts index creation statements—no ALTER TABLE commands for order_items.
This inconsistency means:
- Applying migration 0013 will create quiz indexes but NOT the order_items columns
- The schema/snapshot will be out of sync with the actual database state
- Subsequent migrations or schema introspection may fail
Please verify:
- Are the order_items changes supposed to be in this migration? If so, the SQL file is incomplete.
- Are they from a different migration? If so, the snapshot may be incorrectly generated.
- Was this snapshot accidentally created from a different branch or state?
#!/bin/bash
# Check if order_items schema changes exist in any migration files
echo "=== Searching for order_items ALTER statements in migration files ==="
rg -n "ALTER TABLE.*order_items|selected_size|selected_color" frontend/drizzle/*.sql
echo -e "\n=== Check if order_items schema in TypeScript includes these columns ==="
rg -n "selected_size|selected_color" frontend/db/schema/ -A 2 -B 2
echo -e "\n=== List all migration files to identify where order_items changes belong ==="
ls -la frontend/drizzle/*.sqlAlso applies to: 1450-1476
🤖 Prompt for AI Agents
In @frontend/drizzle/meta/0013_snapshot.json around lines 1371 - 1384, The
snapshot shows order_items additions (columns selected_size and selected_color
and updated unique index order_items_order_variant_uq) but the migration file
0013_warm_dexter_bennett.sql only contains quiz_attempts index DDL; reconcile by
either (A) adding the missing ALTER TABLE ... ADD COLUMN ... NOT NULL DEFAULT ''
statements and the ALTER INDEX/CREATE UNIQUE INDEX/update for
order_items_order_variant_uq into 0013_warm_dexter_bennett.sql (ensuring SQL
matches the snapshot column types/defaults and index definition), or (B) if
those order_items changes belong to a different migration/branch, regenerate or
revert frontend/drizzle/meta/0013_snapshot.json so it matches the actual
migrations (remove selected_size/selected_color and index changes), and run the
ripgrep checks suggested (search for selected_size/selected_color and ALTER
TABLE order_items) to confirm no stray DDL is missing or duplicated; update the
migration or snapshot accordingly and re-run schema generation/tests.
Closes #102
Week 4
Database Performance Optimization
Reduces Neon CU-hours usage for production workloads.
Changes
quiz_attemptstable:quiz_attempts_user_id_idx- optimizes Dashboard queriesquiz_attempts_quiz_id_idx- optimizes quiz statistics queriesPerformance Impact
Migration
0013_warm_dexter_bennett.sql- applies indexes via DrizzleSummary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.