From ad73c824d37b3f5bc733e25c3062e011ccef2755 Mon Sep 17 00:00:00 2001 From: Dotty Dotter Date: Sat, 9 May 2026 02:17:23 +0200 Subject: [PATCH] perf(browser-mem): Polling-Frequenz + Page-Hide-Cleanup (#183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drei Mitigations: 1) Admin-Queue-Polling 5s → 15s. Die Queue ändert sich pro Sekunde ohnehin nicht spürbar; senkt CPU + Network ohne UX-Verlust. 2) ``pagehide``-Listener in admin_queue.html, admin_stand.html und auswertungen.html. Zerstört Chart.js-Instanzen + cleart setInterval- Handles, sobald die Page in den Back/Forward-Cache geht oder geschlossen wird. Bisher hingen sie bis Browser-GC. 3) /auswertungen: zentrales Cleanup für ``_histChart``, alle ``_svCharts.*`` und ``window._zeitreiheModalChart`` beim pagehide. Bisher zerstört nur die einzelnen Render-Funktionen ihre Vorgänger; beim Page-Verlassen blieben sie alle stehen. Was nicht abgedeckt ist (für eventuelle Folge-Iteration mit konkretem Heap-Snapshot): - Lazy-Render lange News-/Drucksachen-Listen via IntersectionObserver - Detaillierte Detached-DOM-Untersuchung pro Seite Bestehende Maßnahmen (bereits da, hier nicht angefasst): chart.destroy() vor jedem neuen Chart, sim.stop() in cluster.html, visibilitychange- Pause für Polling. --- app/templates/v2/screens/admin_queue.html | 12 +++++++++--- app/templates/v2/screens/admin_stand.html | 5 ++++- app/templates/v2/screens/auswertungen.html | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/app/templates/v2/screens/admin_queue.html b/app/templates/v2/screens/admin_queue.html index fe8c2d9..5cfd84d 100644 --- a/app/templates/v2/screens/admin_queue.html +++ b/app/templates/v2/screens/admin_queue.html @@ -216,16 +216,22 @@ async function refresh() { } } +// Polling-Frequenz: 15s (vorher 5s) — die Queue ändert sich pro Sekunde +// ohnehin nicht spürbar, das senkt CPU + Network ohne UX-Verlust (#183). +var _queuePollMs = 15000; refresh(); -let _queueInterval = setInterval(refresh, 5000); -// Pause-Polling wenn Tab versteckt (#183). +let _queueInterval = setInterval(refresh, _queuePollMs); +// Pause-Polling wenn Tab versteckt + beim Verlassen der Page (#183). document.addEventListener('visibilitychange', function () { if (document.hidden) { if (_queueInterval) { clearInterval(_queueInterval); _queueInterval = null; } } else if (!_queueInterval) { refresh(); - _queueInterval = setInterval(refresh, 5000); + _queueInterval = setInterval(refresh, _queuePollMs); } }); +window.addEventListener('pagehide', function () { + if (_queueInterval) { clearInterval(_queueInterval); _queueInterval = null; } +}); {% endblock %} diff --git a/app/templates/v2/screens/admin_stand.html b/app/templates/v2/screens/admin_stand.html index c3fcb98..4c6367a 100644 --- a/app/templates/v2/screens/admin_stand.html +++ b/app/templates/v2/screens/admin_stand.html @@ -330,7 +330,7 @@ async function loadStand() { loadStand(); let _standInterval = setInterval(loadStand, 30000); -// Pause-Polling wenn Tab versteckt (#183). +// Pause-Polling wenn Tab versteckt + beim Verlassen der Page (#183). document.addEventListener('visibilitychange', function () { if (document.hidden) { if (_standInterval) { clearInterval(_standInterval); _standInterval = null; } @@ -339,5 +339,8 @@ document.addEventListener('visibilitychange', function () { _standInterval = setInterval(loadStand, 30000); } }); +window.addEventListener('pagehide', function () { + if (_standInterval) { clearInterval(_standInterval); _standInterval = null; } +}); {% endblock %} diff --git a/app/templates/v2/screens/auswertungen.html b/app/templates/v2/screens/auswertungen.html index 9eb8a56..f84f0bc 100644 --- a/app/templates/v2/screens/auswertungen.html +++ b/app/templates/v2/screens/auswertungen.html @@ -424,6 +424,22 @@ let _histChart = null; let _svCharts = { index: null, heuchelei: null, empfehlung: null, crossBl: null, zeitreihe: null }; let _svMatrixAxis = 'werte'; // 'werte' or 'gruppen' +// Cleanup beim Verlassen der Page (#183) — alle Charts zerstören. +// Verhindert hängende Chart.js-Instanzen + ihre Canvas-Contexts in Memory, +// wenn der Browser die Page nur cachet (zB. Back/Forward-Cache). +window.addEventListener('pagehide', function () { + try { + if (_histChart) { _histChart.destroy(); _histChart = null; } + Object.keys(_svCharts).forEach(function (k) { + if (_svCharts[k]) { try { _svCharts[k].destroy(); } catch (_) {} _svCharts[k] = null; } + }); + if (window._zeitreiheModalChart) { + try { window._zeitreiheModalChart.destroy(); } catch (_) {} + window._zeitreiheModalChart = null; + } + } catch (_) {} +}); + function setMatrixAxis(axis) { _svMatrixAxis = axis; const werteBtn = document.getElementById('sv-axis-werte');