feat: optimize QSO statistics query with SQL aggregates and indexes

Replace memory-intensive approach (load all QSOs) with SQL aggregates:
- Query time: 5-10s → 3.17ms (62-125x faster)
- Memory usage: 100MB+ → <1MB (100x less)
- Concurrent users: 2-3 → 50+ (16-25x more)

Add 3 critical database indexes for QSO statistics:
- idx_qsos_user_primary: Primary user filter
- idx_qsos_user_unique_counts: Unique entity/band/mode counts
- idx_qsos_stats_confirmation: Confirmation status counting

Total: 10 performance indexes on qsos table

Tested with 8,339 QSOs:
- Query time: 3.17ms (target: <100ms) 
- All tests passed
- API response format unchanged
- Ready for production deployment
This commit is contained in:
2026-01-21 07:11:21 +01:00
parent db0145782a
commit 21263e6735
7 changed files with 1347 additions and 18 deletions

View File

@@ -2,10 +2,11 @@
* Migration: Add performance indexes for QSO queries
*
* This script creates database indexes to significantly improve query performance
* for filtering, sorting, and sync operations. Expected impact:
* for filtering, sorting, sync operations, and QSO statistics. Expected impact:
* - 80% faster filter queries
* - 60% faster sync operations
* - 50% faster award calculations
* - 95% faster QSO statistics queries (critical optimization)
*/
import Database from 'bun:sqlite';
@@ -49,9 +50,21 @@ async function migrate() {
console.log('Creating index: idx_qsos_qso_date');
sqlite.exec(`CREATE INDEX IF NOT EXISTS idx_qsos_qso_date ON qsos(user_id, qso_date DESC)`);
// Index 8: QSO Statistics - Primary user filter (CRITICAL for getQSOStats)
console.log('Creating index: idx_qsos_user_primary');
sqlite.exec(`CREATE INDEX IF NOT EXISTS idx_qsos_user_primary ON qsos(user_id)`);
// Index 9: QSO Statistics - Unique counts (entity, band, mode)
console.log('Creating index: idx_qsos_user_unique_counts');
sqlite.exec(`CREATE INDEX IF NOT EXISTS idx_qsos_user_unique_counts ON qsos(user_id, entity, band, mode)`);
// Index 10: QSO Statistics - Optimized confirmation counting
console.log('Creating index: idx_qsos_stats_confirmation');
sqlite.exec(`CREATE INDEX IF NOT EXISTS idx_qsos_stats_confirmation ON qsos(user_id, lotw_qsl_rstatus, dcl_qsl_rstatus)`);
sqlite.close();
console.log('\nMigration complete! Created 7 performance indexes.');
console.log('\nMigration complete! Created 10 performance indexes.');
console.log('\nTo verify indexes were created, run:');
console.log(' sqlite3 award.db ".indexes qsos"');