Speicherverbrauch im Browser optimieren (Charts, D3-Sim, Polling, Listen) #183

Open
opened 2026-05-07 08:56:40 +02:00 by tobias · 2 comments
Owner

Beobachtung: Auf einigen Seiten (vermutlich /auswertungen, /stimmverhalten,
/v2/cluster, /aktuelle-themen) baut sich der Browser-Speicher merklich auf
oder bleibt hoch nach Tab-Wechsel.

Mögliche Quellen (vor der Diagnose)

  • Chart.js-Instanzen — bis zu 7 parallel im Stimmverhalten-Tab. chart.destroy()
    vor neuem new Chart(...) sollte vorhanden sein, aber nicht durchgängig
    geprüft.
  • D3-Force-Simulationen in /v2/cluster — Drag-Handler, Tick-Listener.
    Sim wird beim Wechsel zu anderem Cluster nicht explizit beendet.
  • Polling-Loops/api/queue/status alle 5 s, /api/admin/stand alle 30 s.
    setInterval-Handle wird beim Verlassen der Page ggf. nicht gecleart.
  • html2canvas im Feedback-Widget, zieht den ganzen DOM in Canvas.
  • Live-Polling im Stimmverhalten-Tab (pollQueueUntilDrained) — prüfen
    ob es bei Tab-Wechsel cleared wird.
  • Lange News-/Drucksachen-Listen mit allen Render-States im DOM.

Diagnose-Plan

  1. Chrome-DevTools → Memory-Tab → Heap-Snapshot vor und nach Tab-Wechsel.
  2. Performance-Tab → 1-Minute-Recording mit Tab-Switching.
  3. Welche Detached-DOM-Knoten bleiben? Welche listener bleiben hängen?

Mitigations-Kandidaten

  • chart.destroy() konsequent + _svCharts = {} reset bei Tab-Wechsel.
  • D3-Sim sim.stop() beim Verlassen Cluster-Detail.
  • Polling-Intervalle in Map<elementId, intervalHandle> und cleanup
    bei beforeunload + Tab-Switch.
  • Polling-Frequenz reduzieren (queue/status auf 15 s statt 5 s).
  • Lazy-Render für lange Listen (Intersection-Observer oder Virtualisierung).

Akzeptanz

  • Heap-Snapshot vor/nach Tab-Wechsel zeigt < +5 MB Drift.
  • Keine Detached-DOM-Knoten, die referenziert bleiben.
  • Polling-Intervalle hören auf, wenn die Page verlassen wird.
**Beobachtung:** Auf einigen Seiten (vermutlich `/auswertungen`, `/stimmverhalten`, `/v2/cluster`, `/aktuelle-themen`) baut sich der Browser-Speicher merklich auf oder bleibt hoch nach Tab-Wechsel. ## Mögliche Quellen (vor der Diagnose) - **Chart.js-Instanzen** — bis zu 7 parallel im Stimmverhalten-Tab. `chart.destroy()` vor neuem `new Chart(...)` sollte vorhanden sein, aber nicht durchgängig geprüft. - **D3-Force-Simulationen** in `/v2/cluster` — Drag-Handler, Tick-Listener. Sim wird beim Wechsel zu anderem Cluster nicht explizit beendet. - **Polling-Loops** — `/api/queue/status` alle 5 s, `/api/admin/stand` alle 30 s. `setInterval`-Handle wird beim Verlassen der Page ggf. nicht gecleart. - **html2canvas** im Feedback-Widget, zieht den ganzen DOM in Canvas. - **Live-Polling** im Stimmverhalten-Tab (`pollQueueUntilDrained`) — prüfen ob es bei Tab-Wechsel cleared wird. - **Lange News-/Drucksachen-Listen** mit allen Render-States im DOM. ## Diagnose-Plan 1. Chrome-DevTools → Memory-Tab → Heap-Snapshot vor und nach Tab-Wechsel. 2. Performance-Tab → 1-Minute-Recording mit Tab-Switching. 3. Welche Detached-DOM-Knoten bleiben? Welche listener bleiben hängen? ## Mitigations-Kandidaten - `chart.destroy()` konsequent + `_svCharts = {}` reset bei Tab-Wechsel. - D3-Sim `sim.stop()` beim Verlassen Cluster-Detail. - Polling-Intervalle in `Map<elementId, intervalHandle>` und cleanup bei `beforeunload` + Tab-Switch. - Polling-Frequenz reduzieren (queue/status auf 15 s statt 5 s). - Lazy-Render für lange Listen (Intersection-Observer oder Virtualisierung). ## Akzeptanz - Heap-Snapshot vor/nach Tab-Wechsel zeigt < +5 MB Drift. - Keine Detached-DOM-Knoten, die referenziert bleiben. - Polling-Intervalle hören auf, wenn die Page verlassen wird.
Author
Owner

Erste Fix-Runde durch (commit 481a791):

  • Modal-Chart-Leak in /auswertungen Zeitreihe-Modal: vor Re-Render _zeitreiheModalChart.destroy(), beim Modal-Close + Escape ebenfalls.
  • Cluster-Force-Sim: _currentForceSim global, sim.stop() beim Verlassen Detail-View und vor neuem Render.
  • Polling-Pause auf visibilitychange: queue_widget (5 s), admin_stand (30 s), admin_queue (5 s) — wenn Tab versteckt, kein Poll.

Heap-Snapshot-Verifikation (Playwright headless, 5 Iterationen):

Pfad Baseline Nach 5× Drift
Stimmverhalten Tab-Switch 2.6 MB 5.7 MB +3.1 MB
Cluster-Detail Open/Close 6.0 MB 5.4 MB −0.6 MB

Beide unter dem 5-MB-Akzeptanzkriterium. Cluster-Detail ist sogar negativ — sim.stop() + GC räumen sauber auf.

Was noch offen ist (Folge-Iteration falls nötig):

  • Long-Page-Listen (Drucksachen, News) ohne Virtualisierung
  • html2canvas im Feedback-Widget (zieht ganze DOM ins Canvas — nur bei Feedback-Submit, einmaliger Spike).
  • Audit auf weitere ungebundene Chart-Instanzen wenn neue Charts dazukommen.
**Erste Fix-Runde durch (commit 481a791):** - ✅ **Modal-Chart-Leak** in `/auswertungen` Zeitreihe-Modal: vor Re-Render `_zeitreiheModalChart.destroy()`, beim Modal-Close + Escape ebenfalls. - ✅ **Cluster-Force-Sim**: `_currentForceSim` global, `sim.stop()` beim Verlassen Detail-View und vor neuem Render. - ✅ **Polling-Pause auf `visibilitychange`**: queue_widget (5 s), admin_stand (30 s), admin_queue (5 s) — wenn Tab versteckt, kein Poll. **Heap-Snapshot-Verifikation (Playwright headless, 5 Iterationen):** | Pfad | Baseline | Nach 5× | Drift | |---|---|---|---| | Stimmverhalten Tab-Switch | 2.6 MB | 5.7 MB | **+3.1 MB** ✅ | | Cluster-Detail Open/Close | 6.0 MB | 5.4 MB | **−0.6 MB** ✅ | Beide unter dem 5-MB-Akzeptanzkriterium. Cluster-Detail ist sogar negativ — `sim.stop()` + GC räumen sauber auf. **Was noch offen ist (Folge-Iteration falls nötig):** - Long-Page-Listen (Drucksachen, News) ohne Virtualisierung - html2canvas im Feedback-Widget (zieht ganze DOM ins Canvas — nur bei Feedback-Submit, einmaliger Spike). - Audit auf weitere ungebundene Chart-Instanzen wenn neue Charts dazukommen.
Author
Owner

Pragmatische Mitigations umgesetzt (Commit ad73c82):

  1. Admin-Queue-Polling 5s → 15s.
  2. pagehide-Listener in admin_queue.html, admin_stand.html und auswertungen.html: cleart Interval-Handles + zerstört Chart.js-Instanzen.
  3. Zentraler Chart-Cleanup für /auswertungen beim pagehide (_histChart, _svCharts.*, _zeitreiheModalChart).

Bestehende Maßnahmen unverändert: chart.destroy() vor jedem neuen Chart in /auswertungen + /v2/admin/stand, sim.stop() in cluster.html, visibilitychange-Pause für Polling.

Nicht erschöpfend ohne konkrete Heap-Snapshots: Lazy-Render lange News-/Drucksachen-Listen via IntersectionObserver, detaillierte Detached-DOM-Analyse pro Seite. Wenn Memory-Drift weiter spürbar bleibt, brauchen wir DevTools-Heap-Vergleich; daraus ergeben sich konkrete weitere Fixes.

**Pragmatische Mitigations umgesetzt** (Commit ad73c82): 1) Admin-Queue-Polling 5s → 15s. 2) pagehide-Listener in admin_queue.html, admin_stand.html und auswertungen.html: cleart Interval-Handles + zerstört Chart.js-Instanzen. 3) Zentraler Chart-Cleanup für /auswertungen beim pagehide (_histChart, _svCharts.*, _zeitreiheModalChart). Bestehende Maßnahmen unverändert: chart.destroy() vor jedem neuen Chart in /auswertungen + /v2/admin/stand, sim.stop() in cluster.html, visibilitychange-Pause für Polling. **Nicht erschöpfend** ohne konkrete Heap-Snapshots: Lazy-Render lange News-/Drucksachen-Listen via IntersectionObserver, detaillierte Detached-DOM-Analyse pro Seite. Wenn Memory-Drift weiter spürbar bleibt, brauchen wir DevTools-Heap-Vergleich; daraus ergeben sich konkrete weitere Fixes.
Sign in to join this conversation.
No description provided.