perf(nats): Implement bulk cache prewarming for runners to optimize startup performance
This commit is contained in:
@@ -126,3 +126,65 @@ export async function warmRunner(runnerId: number): Promise<RunnerKVEntry> {
|
||||
await setRunnerEntry(runnerId, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk cache prewarming: loads all runners from the database and populates the KV cache.
|
||||
* Uses 3 efficient queries and parallel KV writes to minimize startup time.
|
||||
*
|
||||
* Call from loader during startup (if NATS_PREWARM=true) to eliminate DB reads on the hot
|
||||
* path from the very first scan.
|
||||
*/
|
||||
export async function warmAll(): Promise<void> {
|
||||
const connection = getConnection();
|
||||
|
||||
// Query 1: All runners
|
||||
const runners = await connection
|
||||
.getRepository(Runner)
|
||||
.createQueryBuilder('runner')
|
||||
.select(['runner.id', 'runner.firstname', 'runner.lastname'])
|
||||
.getMany();
|
||||
|
||||
// Query 2: Total valid distance per runner
|
||||
const distanceResults = await connection
|
||||
.getRepository(TrackScan)
|
||||
.createQueryBuilder('scan')
|
||||
.select('scan.runner', 'runnerId')
|
||||
.addSelect('COALESCE(SUM(track.distance), 0)', 'total')
|
||||
.innerJoin('scan.track', 'track')
|
||||
.where('scan.valid = :valid', { valid: true })
|
||||
.groupBy('scan.runner')
|
||||
.getRawMany();
|
||||
|
||||
// Query 3: Latest valid scan timestamp per runner
|
||||
const latestResults = await connection
|
||||
.getRepository(TrackScan)
|
||||
.createQueryBuilder('scan')
|
||||
.select('scan.runner', 'runnerId')
|
||||
.addSelect('MAX(scan.timestamp)', 'latestTimestamp')
|
||||
.where('scan.valid = :valid', { valid: true })
|
||||
.groupBy('scan.runner')
|
||||
.getRawMany();
|
||||
|
||||
// Build lookup maps
|
||||
const distanceMap = new Map<number, number>();
|
||||
distanceResults.forEach((row: any) => {
|
||||
distanceMap.set(parseInt(row.runnerId, 10), parseInt(row.total, 10));
|
||||
});
|
||||
|
||||
const latestMap = new Map<number, number>();
|
||||
latestResults.forEach((row: any) => {
|
||||
latestMap.set(parseInt(row.runnerId, 10), parseInt(row.latestTimestamp, 10));
|
||||
});
|
||||
|
||||
// Write all entries in parallel
|
||||
const writePromises = runners.map((runner) => {
|
||||
const entry: RunnerKVEntry = {
|
||||
displayName: `${runner.firstname} ${runner.lastname}`,
|
||||
totalDistance: distanceMap.get(runner.id) ?? 0,
|
||||
latestTimestamp: latestMap.get(runner.id) ?? 0,
|
||||
};
|
||||
return setRunnerEntry(runner.id, entry);
|
||||
});
|
||||
|
||||
await Promise.all(writePromises);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user