Traced local_db (bgp_routes/rpki_roas) 2026-07-17: two systemd import timers on Erik have run "successfully" daily since creation (2026-04-28) while doing zero real work -- bgp-importer's Route Views source URL is invalid (parses 0 routes every run, the 36 rows are a one-time manual seed), rpki-importer's feed URLs are both dead and it silently falls back to 5 hardcoded fake ROAs re-upserted daily. The watchdog meant to catch this has itself failed every run since creation (Postgres auth error), so nothing ever alerted. No user-facing endpoint reads from local_db -- only this health check and the unused /api/bgp route do. Gating overall `status` on bgp_routes/rpki_roas row counts meant /api/health reported "degraded" permanently for a dependency nothing actually needs, which could mask a real future degradation behind the noise. Still reports the raw local_db stats (including its own `healthy` sub-field) for diagnostic visibility; only removed it from the top-level status gate. Verified live on Erik: status now reports "ok" when RPKI/ASPA freshness is fine, instead of permanently "degraded".
112 lines
5.3 KiB
JavaScript
112 lines
5.3 KiB
JavaScript
const localDb = require("../../local-db-client");
|
|
const { getRpkiAspaLastFetch, rpkiAspaMap } = require("../services/rpki");
|
|
const { pdbSourceCache } = require("../caches/pdb-source-cache");
|
|
const { roaStore } = require("../caches/roa-store");
|
|
const { ripeStatCache } = require("../services/ripe-stat");
|
|
const { responseCache } = require("../caches/response-caches");
|
|
const { aspaAdoptionDailyHistory } = require("../aspa-adoption/tracker");
|
|
|
|
const BGPROUTES_API_KEY = process.env.BGPROUTES_API_KEY || "";
|
|
|
|
function aspaAdoptionDelta() {
|
|
return aspaAdoptionDailyHistory.length >= 2
|
|
? aspaAdoptionDailyHistory[aspaAdoptionDailyHistory.length - 1].aspa_objects - aspaAdoptionDailyHistory[aspaAdoptionDailyHistory.length - 2].aspa_objects
|
|
: 0;
|
|
}
|
|
|
|
// Health endpoint — extended with cache status, ASPA metrics, and local DB stats
|
|
function handleHealth(req, res) {
|
|
const mem = process.memoryUsage();
|
|
const rpkiAspaLastFetch = getRpkiAspaLastFetch();
|
|
const aspaAge = rpkiAspaLastFetch ? Math.floor((Date.now() - rpkiAspaLastFetch) / 60000) : -1;
|
|
// Bug fix 2026-07-17: previously only declared inside the .then() callback
|
|
// below, so the .catch() branch's reference to it threw ReferenceError
|
|
// (never caught -- the response just hung until client timeout).
|
|
const roaAge = roaStore.lastBuild ? Math.floor((Date.now() - roaStore.lastBuild) / 60000) : -1;
|
|
const pdbTotal = pdbSourceCache.hits + pdbSourceCache.misses;
|
|
|
|
// Query local DB stats (async, but return partial if needed)
|
|
localDb.getLocalDbStats().then(function(dbStats) {
|
|
// hasLocalBgp/hasLocalRpki reflect a >2026-04 prototype (bgp_routes/
|
|
// rpki_roas import pipeline) confirmed vestigial 2026-07-17: both import
|
|
// timers have run "successfully" daily since creation while doing zero
|
|
// real work (dead source URL / silent hardcoded-fallback), and no
|
|
// user-facing endpoint reads from this local DB -- only this health
|
|
// check and the unused /api/bgp route do. Still reported below as
|
|
// diagnostic info, but no longer allowed to hold overall `status` at a
|
|
// permanent false "degraded" for a dependency nothing actually needs.
|
|
const hasLocalBgp = dbStats && dbStats.bgp_routes > 100000; // should have >2M rows normally
|
|
const hasLocalRpki = dbStats && dbStats.rpki_roas > 100000; // should have >500k rows normally
|
|
const status = aspaAge < 300 ? "ok" : "degraded";
|
|
|
|
const healthResponse = {
|
|
status,
|
|
service: "PeerCortex",
|
|
version: "0.6.9",
|
|
timestamp: new Date().toISOString(),
|
|
uptime_seconds: Math.floor(process.uptime()),
|
|
memory_mb: Math.round(mem.heapUsed / 1024 / 1024),
|
|
bgproutes_configured: !!BGPROUTES_API_KEY,
|
|
caches: {
|
|
aspa_map: { entries: rpkiAspaMap.size, age_minutes: aspaAge },
|
|
pdb_net: { entries: pdbSourceCache.net.size, hit_rate_pct: pdbTotal > 0 ? Math.round(pdbSourceCache.hits / pdbTotal * 100) : 0 },
|
|
pdb_netixlan: { entries: pdbSourceCache.netixlan.size },
|
|
pdb_netfac: { entries: pdbSourceCache.netfac.size },
|
|
ripe_stat: { entries: ripeStatCache.size },
|
|
response_cache: { entries: responseCache.size },
|
|
},
|
|
local_db: dbStats ? {
|
|
bgp_routes: dbStats.bgp_routes,
|
|
rpki_roas: dbStats.rpki_roas,
|
|
threat_intel: dbStats.threat_intel,
|
|
rdap_cache_entries: dbStats.rdap_cache_entries,
|
|
source: "PostgreSQL (local)",
|
|
healthy: hasLocalBgp && hasLocalRpki,
|
|
} : null,
|
|
aspa_adoption: {
|
|
total_objects: rpkiAspaMap.size,
|
|
roa_count: roaStore.count,
|
|
history_samples: aspaAdoptionDailyHistory.length,
|
|
delta_last: aspaAdoptionDelta(),
|
|
},
|
|
};
|
|
return res.end(JSON.stringify(healthResponse, null, 2));
|
|
}).catch(function(e) {
|
|
console.error('[/api/health] Local DB stats error:', e.message);
|
|
// Return health without local DB stats on error. Local DB stats failed
|
|
// to fetch, so "ok" can't be confirmed -- report degraded, matching the
|
|
// honest-degradation pattern used throughout this codebase.
|
|
const status = "degraded";
|
|
return res.end(
|
|
JSON.stringify({
|
|
status,
|
|
service: "PeerCortex",
|
|
version: "0.6.9",
|
|
timestamp: new Date().toISOString(),
|
|
uptime_seconds: Math.floor(process.uptime()),
|
|
memory_mb: Math.round(mem.heapUsed / 1024 / 1024),
|
|
bgproutes_configured: !!BGPROUTES_API_KEY,
|
|
caches: {
|
|
roa_store: { entries: roaStore.count, age_minutes: roaAge, ready: roaStore.ready },
|
|
aspa_map: { entries: rpkiAspaMap.size, age_minutes: aspaAge },
|
|
pdb_net: { entries: pdbSourceCache.net.size, hit_rate_pct: pdbTotal > 0 ? Math.round(pdbSourceCache.hits / pdbTotal * 100) : 0 },
|
|
pdb_netixlan: { entries: pdbSourceCache.netixlan.size },
|
|
pdb_netfac: { entries: pdbSourceCache.netfac.size },
|
|
ripe_stat: { entries: ripeStatCache.size },
|
|
response_cache: { entries: responseCache.size },
|
|
},
|
|
local_db: { error: "Could not fetch local DB stats", message: e.message },
|
|
aspa_adoption: {
|
|
total_objects: rpkiAspaMap.size,
|
|
roa_count: roaStore.count,
|
|
history_samples: aspaAdoptionDailyHistory.length,
|
|
delta_last: aspaAdoptionDelta(),
|
|
},
|
|
}, null, 2)
|
|
);
|
|
});
|
|
return;
|
|
}
|
|
|
|
module.exports = { handleHealth };
|