fix(phase26): abort preload fetches when switching trips to prevent server freeze

This commit is contained in:
Mathieu Moran
2026-08-11 05:01:18 -04:00
parent ad56380e23
commit cdb3fc19c7
+12 -9
View File
@@ -33,7 +33,8 @@
let _geoTimer = null; let _geoTimer = null;
let _lastGeoKey = null; let _lastGeoKey = null;
let _geoCache = {}; // key → label, pre-populated on trip load let _geoCache = {};
let _preloadController = null;
function _geoKey(lat, lon) { function _geoKey(lat, lon) {
const la = typeof lat === 'number' ? lat : parseFloat(lat); const la = typeof lat === 'number' ? lat : parseFloat(lat);
@@ -43,16 +44,17 @@
} }
function preloadGeocodes(points) { function preloadGeocodes(points) {
// Pre-fetch geocode labels for all unique coordinates with a small // Abort any in-flight preload from a previous trip selection.
// concurrency cap so the server (which uses slow Nominatim) doesn't if (_preloadController) _preloadController.abort();
// get overwhelmed. The Pi's reverse_geocoder is instant, so this _preloadController = new AbortController();
// still works fast there. const signal = _preloadController.signal;
_geoCache = {}; _geoCache = {};
const seen = new Set(); const seen = new Set();
const queue = []; const queue = [];
for (const p of points) { for (const p of points) {
const key = _geoKey(p.latitude, p.longitude); const key = _geoKey(p.latitude, p.longitude);
if (seen.has(key)) continue; if (!key || seen.has(key)) continue;
seen.add(key); seen.add(key);
queue.push({ key, lat: p.latitude, lon: p.longitude }); queue.push({ key, lat: p.latitude, lon: p.longitude });
} }
@@ -61,17 +63,18 @@
const CONCURRENCY = 2; const CONCURRENCY = 2;
let idx = 0; let idx = 0;
function next() { function next() {
if (idx >= queue.length) return; if (idx >= queue.length || signal.aborted) return;
const item = queue[idx++]; const item = queue[idx++];
const ctrl = new AbortController(); const ctrl = new AbortController();
const t = setTimeout(() => ctrl.abort(), 3000); const t = setTimeout(() => ctrl.abort(), 3000);
signal.addEventListener('abort', () => ctrl.abort());
fetch(`/api/geocode?lat=${item.lat}&lon=${item.lon}`, { signal: ctrl.signal }) fetch(`/api/geocode?lat=${item.lat}&lon=${item.lon}`, { signal: ctrl.signal })
.then(r => { clearTimeout(t); return r.ok ? r.json() : null; }) .then(r => { clearTimeout(t); return r.ok ? r.json() : null; })
.then(data => { .then(data => {
_geoCache[item.key] = (data && data.label) || null; if (!signal.aborted) _geoCache[item.key] = (data && data.label) || null;
}) })
.catch(() => { clearTimeout(t); }) .catch(() => { clearTimeout(t); })
.finally(() => next()); .finally(() => { if (!signal.aborted) next(); });
} }
for (let i = 0; i < CONCURRENCY && i < queue.length; i++) next(); for (let i = 0; i < CONCURRENCY && i < queue.length; i++) next();
} }