const axios = require('axios'); const cache = require('./cache'); const db = require('./db'); const POLL_INTERVAL = 10000; // 10 seconds async function pollLatency() { try { const [routes] = await db.query(` SELECT r.*, s.url FROM latency_routes r JOIN prometheus_sources s ON r.source_id = s.id WHERE s.type = 'blackbox' `); if (routes.length === 0) return; // Poll each route await Promise.allSettled(routes.map(async (route) => { try { // Blackbox exporter probe URL // We assume ICMP module for now. If target is a URL, maybe use http_2xx let module = 'icmp'; let target = route.latency_target; if (target.startsWith('http://') || target.startsWith('https://')) { module = 'http_2xx'; } const probeUrl = `${route.url.replace(/\/+$/, '')}/probe?module=${module}&target=${encodeURIComponent(target)}`; const startTime = Date.now(); const response = await axios.get(probeUrl, { timeout: 5000 }); // 1. Parse prometheus text format for success and specific metrics let latency = null; let success = false; const lines = response.data.split('\n'); for (const line of lines) { if (line.match(/^probe_success\s+1/)) { success = true; break; } } if (success) { // Try specialized metrics first for better accuracy const priorityMetrics = ['probe_icmp_duration_seconds', 'probe_http_duration_seconds', 'probe_duration_seconds']; for (const metricName of priorityMetrics) { const regex = new RegExp(`^${metricName}(?:\\{.*\\})?\\s+([\\d.]+)`); for (const line of lines) { const match = line.match(regex); if (match) { latency = parseFloat(match[1]) * 1000; // to ms break; } } if (latency !== null) break; } } // Save to Valkey await cache.set(`latency:route:${route.id}`, latency, 60); // console.log(`[Latency] Route ${route.id} (${target}): ${latency.toFixed(2)}ms`); } catch (err) { // console.error(`[Latency] Error polling route ${route.id}:`, err.message); await cache.set(`latency:route:${route.id}`, null, 60); } })); } catch (err) { console.error('[Latency] Service error:', err.message); } } let intervalId = null; function start() { if (intervalId) clearInterval(intervalId); pollLatency(); // initial run intervalId = setInterval(pollLatency, POLL_INTERVAL); console.log('[Latency] Background service started (polling Blackbox Exporter directly)'); } module.exports = { start };