Self-healing results backfill: re-fetch past match days at boot
The persistence table shipped empty just as ESPN's default scoreboard window rolled past matchday 1 — prod forgot both results. Rather than patching the DB by hand, boot now re-fetches the date-scoped scoreboard for any past day whose fixtures still look scheduled and merges + persists the results. Heals fresh databases and any future data loss the same way. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -113,6 +113,31 @@ export function startScheduler(
|
||||
}
|
||||
};
|
||||
|
||||
// Self-healing results backfill: any PAST match day whose fixtures still
|
||||
// look 'scheduled' (fresh DB, or results lost before persistence existed)
|
||||
// gets its date-scoped ESPN scoreboard re-fetched and merged.
|
||||
const resultsBackfill = async (): Promise<void> => {
|
||||
const today = dateUTC(new Date().toISOString());
|
||||
const dates = new Set<string>();
|
||||
for (const f of state.allFixtures()) {
|
||||
const d = dateUTC(f.kickoff);
|
||||
if (d < today && f.status === 'scheduled' && f.home.team && f.away.team) dates.add(d);
|
||||
}
|
||||
for (const date of dates) {
|
||||
if (stopped) return;
|
||||
const t0 = Date.now();
|
||||
try {
|
||||
const matches = await fetchEspnScoreboard(date);
|
||||
const changed = state.mergeLive(matches, 'espn');
|
||||
for (const f of changed) saveFixtureResult(f.num, f.homeScore, f.awayScore, f.status);
|
||||
if (changed.length) onChange();
|
||||
logIngest('espn', 'results-backfill', true, Date.now() - t0, `${date}: ${changed.length} restored`);
|
||||
} catch (e) {
|
||||
logIngest('espn', 'results-backfill', false, Date.now() - t0, e instanceof Error ? e.message : String(e));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// One-time backfill: re-store summaries saved by an older normalizer (e.g.
|
||||
// before structured scorers existed), so started matches stay fully usable.
|
||||
const backfillExt = async (): Promise<void> => {
|
||||
@@ -339,6 +364,7 @@ export function startScheduler(
|
||||
await mapAllFixtures().catch(() => {});
|
||||
await liveTick().catch(() => {});
|
||||
scheduleLive(); // live cadence starts now; slow one-time sweeps follow
|
||||
await resultsBackfill().catch(() => {}); // heal any forgotten past match days
|
||||
// persist whatever the current scoreboard window knows — results learned
|
||||
// before this table existed would otherwise never be written
|
||||
for (const f of state.allFixtures()) {
|
||||
|
||||
Reference in New Issue
Block a user