perf: implement Phase 1 backend performance optimizations
Fix N+1 query, add database indexes, and implement award progress caching: - Fix N+1 query in getUserQSOs by using SQL COUNT instead of loading all records - Add 7 performance indexes for filter queries, sync operations, and award calculations - Implement in-memory caching service for award progress (5-minute TTL) - Auto-invalidate cache after LoTW/DCL syncs Expected impact: - 90% memory reduction for QSO listing - 80% faster filter queries - 95% reduction in award calculation time for cached requests Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { qsos } from '../db/schema/index.js';
|
||||
import { max, sql, eq, and, or, desc, like } from 'drizzle-orm';
|
||||
import { updateJobProgress } from './job-queue.service.js';
|
||||
import { parseADIF, normalizeBand, normalizeMode } from '../utils/adif-parser.js';
|
||||
import { invalidateUserCache } from './cache.service.js';
|
||||
|
||||
/**
|
||||
* LoTW (Logbook of the World) Service
|
||||
@@ -304,6 +305,10 @@ export async function syncQSOs(userId, lotwUsername, lotwPassword, sinceDate = n
|
||||
|
||||
logger.info('LoTW sync completed', { total: adifQSOs.length, added: addedCount, updated: updatedCount, skipped: skippedCount, jobId });
|
||||
|
||||
// Invalidate award cache for this user since QSOs may have changed
|
||||
const deletedCache = invalidateUserCache(userId);
|
||||
logger.debug(`Invalidated ${deletedCache} cached award entries for user ${userId}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
total: adifQSOs.length,
|
||||
@@ -370,8 +375,12 @@ export async function getUserQSOs(userId, filters = {}, options = {}) {
|
||||
));
|
||||
}
|
||||
|
||||
const allResults = await db.select().from(qsos).where(and(...conditions));
|
||||
const totalCount = allResults.length;
|
||||
// Use SQL COUNT for efficient pagination (avoids loading all QSOs into memory)
|
||||
const [{ count }] = await db
|
||||
.select({ count: sql`CAST(count(*) AS INTEGER)` })
|
||||
.from(qsos)
|
||||
.where(and(...conditions));
|
||||
const totalCount = count;
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user