Skip to content

Commit 75a287d

Browse files
committed
fix: prune GTFS dataset entries with missing files at the start of every Transitous run
Without this, a previous crash that upserted a `.tmp-<id>` entry into the registry — or any operator-level archive deletion — would persist indefinitely. The pipeline only sweeps the store via `replaceType` on a fully-successful run; the resume (failure) path leaves it alone so that partial state survives. Self-heal at the start of every run by dropping any GTFS dataset whose `path` no longer exists on disk. Idempotent, free when there's nothing stale.
1 parent 5893fe8 commit 75a287d

1 file changed

Lines changed: 21 additions & 0 deletions

File tree

services/data-manager/src/jobs/transitous-pipeline.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,22 @@ function datasetIdFromArchive(name: string): string {
442442
return name.replace(/\.zip$/i, "");
443443
}
444444

445+
/**
446+
* Remove store entries for GTFS feeds whose archive no longer exists on
447+
* disk. Called at the top of every Transitous run so a previous crash that
448+
* left a `.tmp-*` upsert behind, or an operator-deleted archive, doesn't
449+
* stay in the registry forever — the resume (failure) path of the pipeline
450+
* never replaces the store wholesale, so without this nudge stale entries
451+
* persist indefinitely.
452+
*/
453+
function pruneOrphanedGtfsDatasets(store: StateStore): void {
454+
for (const dataset of store.getAll()) {
455+
if (dataset.type !== "gtfs") continue;
456+
if (existsSync(dataset.path)) continue;
457+
store.remove("gtfs", dataset.id);
458+
}
459+
}
460+
445461
function scanGtfsArchives(gtfsDir: string): GtfsArchiveSnapshot[] {
446462
if (!existsSync(gtfsDir)) return [];
447463
return readdirSync(gtfsDir)
@@ -486,6 +502,11 @@ export async function downloadGtfsViaTransitous(
486502
);
487503
try {
488504
ensureTransitousWorkdirs(catalogDir, gtfsDir, downloadsDir);
505+
// Drop any GTFS dataset entries whose archive is no longer on disk.
506+
// Keeps the registry self-healing across crashes / .tmp-* renames /
507+
// operator deletions, even when the pipeline ends in the resume
508+
// (failure) branch and never gets to wholesale-replace the store.
509+
pruneOrphanedGtfsDatasets(opts.store);
489510
applyApiKeysOverlay(
490511
catalogDir,
491512
opts.apiKeysPath ?? process.env.TRANSITOUS_API_KEYS_PATH ?? DEFAULT_TRANSITOUS_API_KEYS_PATH,

0 commit comments

Comments
 (0)