Zuerst gemessen, dann gedreht - und das Backend war NICHT das Problem:
/api/snapshot 75 Abrufe in 20 s -> 21 verschiedene ts = ~1/s
WebSocket-Push direkt am Socket gemessen: Median 1,04 s (min 1,00 max 1,06)
G/V-Aenderungen Ø 1,44 s - das ist der Markt, keine Verzoegerung
Die ganze Server-Kette liefert bereits 1 s. Der Lag entsteht erst am Handy:
bricht der WebSocket STILL weg (WireGuard-Aussetzer, App-Wechsel), traegt nur
noch der REST-Poll - und der stand auf 4 s.
POLL_MS 4000 -> 1000, aber mit Sperre: solange der WS liefert (lastWsMs,
Fenster WS_FRISCH_MS=2200 ms), ueberspringt der Poll den Abruf. Kein
zusaetzlicher Verkehr im Normalfall.
Denkfehler, den erst die Simulation zeigte: der erste Entwurf las die
WS-Frische aus lastRecvMs (jede Quelle) - damit drosselte sich der Poll SELBST
und kam bei totem WS nur auf 2,9 s statt 1 s. Jetzt zwei getrennte Uhren.
Simuliert: WS gesund -> 0 Zusatz-Abrufe, max. Alter 0,9 s. WS tot -> 1/s, max.
Alter 0,9 s. WS traege (5 s) -> max. 2,9 s. Vorher bei totem WS: 4,0 s.
Der Verbindungspunkt log: er wurde nur bei sock.onclose auf "off" gesetzt, aber
genau der Fall, der den Lag erzeugt, feuert KEIN onclose - der Socket haengt
still, der Punkt blieb gruen. Neu .dot.stale (bernstein, pulsierend) ab 3,5 s
ohne frischen Snapshot: gemessen wird, was ANKOMMT, nicht was der Socket ueber
sich behauptet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>