fix(cache): use Promise.allSettled to prevent hung promises from blocking scan

Root cause: Promise.all() waits for ALL promises, so a single hung/slow request
blocks the entire batch. With 5001 promises and 16 concurrent limit, timeouts
cause cascading delays that appear as 'scan stopped'.

Fix:
- Extract processSingleActivity() helper function
- Use Promise.allSettled() instead of Promise.all()
- Each promise handles its own success/error counting
- Prevents single hung promise from blocking entire scan

Impact: Scan should now complete all 5001 IDs without getting stuck
This commit is contained in:
JamesFlare1212
2026-04-06 21:48:10 -04:00
parent 5f630f8599
commit f1967d5519

View File

@@ -91,6 +91,24 @@ async function processAndCacheActivity(activityId: string): Promise<ActivityData
} }
} }
/**
* Process a single activity for initialization (extracted for Promise.allSettled)
* @param activityId - The activity ID to process
*/
async function processSingleActivity(activityId: string): Promise<void> {
const cachedData = await getActivityData(activityId);
if (!cachedData ||
Object.keys(cachedData).length === 0 ||
!cachedData.lastCheck ||
cachedData.error) {
logger.debug(`Initializing cache for activity ID: ${activityId}`);
await processAndCacheActivity(activityId);
}
// else: skip (already cached)
}
/** /**
* Initialize the club cache by scanning through all activity IDs * Initialize the club cache by scanning through all activity IDs
*/ */
@@ -107,42 +125,30 @@ export async function initializeClubCache(): Promise<void> {
for (let i = MIN_ACTIVITY_ID_SCAN; i <= MAX_ACTIVITY_ID_SCAN; i++) { for (let i = MIN_ACTIVITY_ID_SCAN; i <= MAX_ACTIVITY_ID_SCAN; i++) {
const activityId = String(i); const activityId = String(i);
promises.push(limit(async () => { promises.push(
try { limit(() =>
const cachedData = await getActivityData(activityId); processSingleActivity(activityId)
.then(() => {
if (!cachedData ||
Object.keys(cachedData).length === 0 ||
!cachedData.lastCheck ||
cachedData.error) {
logger.debug(`Initializing cache for activity ID: ${activityId}`);
await processAndCacheActivity(activityId);
successCount++; successCount++;
} else {
skippedCount++;
}
processedCount++; processedCount++;
// Log progress every 100 activities
if (processedCount % 100 === 0) { if (processedCount % 100 === 0) {
logger.info(`Progress: ${processedCount}/${totalIds} (${Math.round(processedCount/totalIds*100)}%) - Success: ${successCount}, Skipped: ${skippedCount}, Errors: ${errorCount}`); logger.info(`Progress: ${processedCount}/${totalIds} (${Math.round(processedCount/totalIds*100)}%) - Success: ${successCount}, Skipped: ${skippedCount}, Errors: ${errorCount}`);
} }
})
} catch (error) { .catch((error: unknown) => {
errorCount++; errorCount++;
processedCount++; processedCount++;
logger.error(`Error processing activity ID ${activityId}:`, error); logger.error(`Error processing activity ID ${activityId}:`, error);
if (processedCount % 100 === 0) { if (processedCount % 100 === 0) {
logger.info(`Progress: ${processedCount}/${totalIds} (${Math.round(processedCount/totalIds*100)}%) - Success: ${successCount}, Skipped: ${skippedCount}, Errors: ${errorCount}`); logger.info(`Progress: ${processedCount}/${totalIds} (${Math.round(processedCount/totalIds*100)}%) - Success: ${successCount}, Skipped: ${skippedCount}, Errors: ${errorCount}`);
} }
} })
})); )
);
} }
await Promise.all(promises); // Use allSettled to prevent single hung promise from blocking all
await Promise.allSettled(promises);
logger.info(`Initial club cache population finished.`); logger.info(`Initial club cache population finished.`);
logger.info(`Summary: Total: ${totalIds}, Processed: ${processedCount}, Success: ${successCount}, Skipped: ${skippedCount}, Errors: ${errorCount}`); logger.info(`Summary: Total: ${totalIds}, Processed: ${processedCount}, Success: ${successCount}, Skipped: ${skippedCount}, Errors: ${errorCount}`);