Bug 106% raceCondition
This commit is contained in:
58
doc/09_Bug_reports.md
Normal file
58
doc/09_Bug_reports.md
Normal file
@@ -0,0 +1,58 @@
|
||||
## Multi-User ##
|
||||
|
||||
Wenn zwei (oder mehr) User streamen, dann kann kein High-Res Bild mehr
|
||||
gemacht werden. Das Problem: Der Stream bleibt irgendwo aufrecht erhalten.
|
||||
|
||||
|
||||
Fix-Vorschlag Option 1: Alle Browser erhalten bei einem High-Res Request
|
||||
die Anweisung, den Stream abzumelden.
|
||||
|
||||
Fix-Vorschlag Option 2: Der Stream wird umgeleitet zu einer Zwischenstelle "Schalter" und erst von dort aus an die Browser verteilt. Dann wird die
|
||||
"unterbrechung" bzw. "umleitung" des Streams serverseitig erledigt.
|
||||
|
||||
|
||||
|
||||
|
||||
## BugReport 106% ##
|
||||
|
||||
Anmelden abmelden funktioniert. Es bleibt bei 35% Prozessor-Last
|
||||
|
||||
Anmelden Screenshot funktioniert. Es bleibt bei 35% Prozessor-Last
|
||||
|
||||
Anmelden Screenshot neu anmelden > Es gibt 108% Prozessor-Last und ein Stream friert ein.
|
||||
|
||||
|
||||
Wenn ich den Container restarte, funktioniert es.
|
||||
|
||||
siehe auch doc/04_Delay_roadmap.md sowie doc/05_screenshot_roadmap.md
|
||||
|
||||
### Root Cause (2026-06-05)
|
||||
|
||||
**Race condition:** `cam_hires` und `cam` belegen kurz gleichzeitig dasselbe `/dev/videoN`.
|
||||
|
||||
```
|
||||
1. stopStream(cam0) → 0 Consumer → go2rtc stoppt cam0-FFmpeg → /dev/video0 frei
|
||||
2. Server: frame.jpeg → go2rtc startet cam0_hires-FFmpeg auf /dev/video0 (1280×960)
|
||||
3. Response fertig → go2rtc sendet SIGTERM an cam0_hires-FFmpeg
|
||||
4. Server antwortet Client (OHNE auf FFmpeg-Exit zu warten!) ← Fehler
|
||||
5. Client sleep(600ms) → startStream(cam0) → go2rtc startet cam0-FFmpeg auf /dev/video0
|
||||
⚠ cam0_hires-FFmpeg hält /dev/video0 noch offen → 2 FFmpeg auf 1 Device → 108%
|
||||
```
|
||||
|
||||
Der Konflikt in Schritt 5 versetzt `cam0_hires` in einen Fehlerzustand in go2rtc.
|
||||
Beim nächsten Reconnect ("neu anmelden") startet go2rtc's Retry `cam0_hires` erneut —
|
||||
gleichzeitig mit `cam0` → wieder 108% + Stream friert ein.
|
||||
|
||||
### Fix (umgesetzt in src/snapshotService.js)
|
||||
|
||||
Server wartet nach dem frame.jpeg-Fetch, bis `cam_hires`-Producer in go2rtc wirklich
|
||||
gestoppt ist (`pRunning=false`, `nConsumers=0`), plus 400ms Puffer für den FFmpeg-Exit.
|
||||
Erst dann geht die Antwort zum Client → `/dev/videoN` ist garantiert frei wenn
|
||||
`startStream(cam)` startet.
|
||||
|
||||
### Noch offen: Multi-User (siehe Abschnitt oben)
|
||||
|
||||
Das Multi-User-Problem bleibt. Bei ≥2 aktiven Clients kann `/hires` nicht starten,
|
||||
weil der `/hires`-Endpoint wartet bis `cam` 0 Consumer hat (max 8s), aber ein
|
||||
zweiter Browser die Consumer-Zahl nie auf 0 sinken lässt → Timeout → 503.
|
||||
Fix-Optionen: siehe Multi-User-Abschnitt oben.
|
||||
@@ -114,6 +114,30 @@ function createSnapshotRouter(go2rtcUrl) {
|
||||
return res.status(503).json({ error: 'kein verwertbarer Hi-Res-Frame (Warmup-Timeout)' });
|
||||
}
|
||||
|
||||
// Schritt 3: Warten bis cam_hires Producer gestoppt ist bevor Client cam0 reconnectet.
|
||||
// Ohne dieses Warten: cam_hires-FFmpeg hält /dev/videoN noch offen, wenn startStream(cam)
|
||||
// go2rtc's cam-Producer startet → Race, zwei FFmpeg auf demselben Device → 108% CPU.
|
||||
{
|
||||
const t2 = Date.now();
|
||||
while (Date.now() - t2 < 5000) {
|
||||
try {
|
||||
const rp = await fetch(`${go2rtcUrl}/api/streams`, { signal: AbortSignal.timeout(1000) });
|
||||
if (rp.ok) {
|
||||
const ss = await rp.json();
|
||||
const sh = ss[hiresId];
|
||||
const nCh = sh ? (sh.consumers ?? []).length : 0;
|
||||
const pHRunning = sh ? (sh.producers ?? []).some(p => (p.state ?? '') === 'running') : false;
|
||||
if (nCh === 0 && !pHRunning) {
|
||||
console.log(`[hires][${id}] cam_hires gestoppt nach ${Date.now() - t2}ms – Gerät frei`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (_e) { /* ignore */ }
|
||||
await sleep(300);
|
||||
}
|
||||
await sleep(400); // Puffer: FFmpeg-Prozess-Exit bis Kernel Device-FD freigibt
|
||||
}
|
||||
|
||||
console.log(`[hires][${id}] OK – ${jpeg.length} bytes, Breite=${lastWidth}, Dauer=${Date.now() - t0}ms`);
|
||||
res.set({
|
||||
'Content-Type': 'image/jpeg',
|
||||
|
||||
Reference in New Issue
Block a user