Die Abarbeitungsansicht fragt alle zehn Sekunden nach dem Zustand. Das reicht für zwei Techniker an
einem Lauf, kostet aber bei zwanzig offenen Tabs zwanzig Abfragen je zehn Sekunden — und zehn
Sekunden sind lang, wenn zwei Leute dieselbe Liste abhaken.
Die Vorleistung steht: das Polling liegt in genau einem Route Handler
(/api/laeufe/[id]/zustand) und lässt sich tauschen.
Zu entscheiden
LISTEN/NOTIFY in Postgres als Signal — Empfehlung. Ein Trigger auf Schrittergebnis
und Lauf schickt NOTIFY lauf_geaendert, '<laufId>'; der Route Handler hält eine Verbindung
und schiebt weiter. Damit funktioniert es auch mit mehreren Instanzen der Anwendung, ohne
einen zweiten Dienst. Die Alternative — der Server fragt selbst alle zwei Sekunden — wäre
dasselbe Polling, nur an anderer Stelle.
Polling bleibt als Rückfall, nicht als zweite Wahrheit: derselbe Handler antwortet weiter
auf eine gewöhnliche Anfrage. Wer kein EventSource hat oder hinter einem Proxy sitzt, der
Ströme puffert, bekommt den alten Weg. Ein Rückfall, der nie geprüft wird, ist keiner —
deshalb ein Test je Weg.
Aufgaben
Trigger und NOTIFY in einer handgeschriebenen Migration, mit dem üblichen Vermerk (Prisma
kennt Trigger nicht und will sie löschen)
Route Handler liefert text/event-stream, schickt alle 25 s einen Kommentar als Herzschlag
(sonst schließen Proxys die Verbindung) und beendet sauber bei abort
Der Handler prüft Recht und Mandant selbst — ein offener Strom ist ein Leseweg wie jeder
andere (CLAUDE.md: Route Handler deckt die Hülle nicht ab)
Client: EventSource mit Wiederverbindung; bei versteckter Lasche pausieren wie heute; bei error auf Polling zurückfallen und das einmal melden, nicht bei jedem Versuch
docs/betrieb/reverse-proxy.md: proxy_buffering off für nginx, flush_interval -1 für
Caddy — ohne das kommt kein Ereignis an, und die Seite sieht aus, als sei sie hängen geblieben
Obergrenze für gleichzeitige Ströme je Instanz, damit ein Browser mit vierzig Tabs die
Verbindungen nicht aufbraucht
Akzeptanzkriterien
Test: zwei Sitzungen am selben Lauf — hakt die eine ab, sieht die andere es in unter einer
Sekunde, ohne neu zu laden
Test: ohne Recht wird der Strom abgewiesen, bevor das erste Ereignis fließt
Test: ein Lauf eines fremden Kunden liefert keinen Strom
Test: der Rückfall auf Polling funktioniert — gefahren mit abgeschaltetem EventSource
Der Herzschlag kommt an: eine Verbindung überlebt 60 s ohne Änderung
Quelle im Repo: docs/issues/033-sse-live.md
## Ziel
Die Abarbeitungsansicht fragt alle zehn Sekunden nach dem Zustand. Das reicht für zwei Techniker an
einem Lauf, kostet aber bei zwanzig offenen Tabs zwanzig Abfragen je zehn Sekunden — und zehn
Sekunden sind lang, wenn zwei Leute dieselbe Liste abhaken.
Die Vorleistung steht: das Polling liegt in genau einem Route Handler
(`/api/laeufe/[id]/zustand`) und lässt sich tauschen.
## Zu entscheiden
- [ ] **`LISTEN`/`NOTIFY` in Postgres als Signal** — Empfehlung. Ein Trigger auf `Schrittergebnis`
und `Lauf` schickt `NOTIFY lauf_geaendert, '<laufId>'`; der Route Handler hält eine Verbindung
und schiebt weiter. Damit funktioniert es auch mit mehreren Instanzen der Anwendung, ohne
einen zweiten Dienst. Die Alternative — der Server fragt selbst alle zwei Sekunden — wäre
dasselbe Polling, nur an anderer Stelle.
- [ ] **Polling bleibt als Rückfall**, nicht als zweite Wahrheit: derselbe Handler antwortet weiter
auf eine gewöhnliche Anfrage. Wer kein `EventSource` hat oder hinter einem Proxy sitzt, der
Ströme puffert, bekommt den alten Weg. Ein Rückfall, der nie geprüft wird, ist keiner —
deshalb ein Test je Weg.
## Aufgaben
- [ ] Trigger und `NOTIFY` in einer handgeschriebenen Migration, mit dem üblichen Vermerk (Prisma
kennt Trigger nicht und will sie löschen)
- [ ] Route Handler liefert `text/event-stream`, schickt alle 25 s einen Kommentar als Herzschlag
(sonst schließen Proxys die Verbindung) und beendet sauber bei `abort`
- [ ] **Der Handler prüft Recht und Mandant selbst** — ein offener Strom ist ein Leseweg wie jeder
andere (CLAUDE.md: Route Handler deckt die Hülle nicht ab)
- [ ] Client: `EventSource` mit Wiederverbindung; bei versteckter Lasche pausieren wie heute; bei
`error` auf Polling zurückfallen und das **einmal** melden, nicht bei jedem Versuch
- [ ] `docs/betrieb/reverse-proxy.md`: `proxy_buffering off` für nginx, `flush_interval -1` für
Caddy — ohne das kommt kein Ereignis an, und die Seite sieht aus, als sei sie hängen geblieben
- [ ] Obergrenze für gleichzeitige Ströme je Instanz, damit ein Browser mit vierzig Tabs die
Verbindungen nicht aufbraucht
## Akzeptanzkriterien
- [ ] Test: zwei Sitzungen am selben Lauf — hakt die eine ab, sieht die andere es in unter einer
Sekunde, ohne neu zu laden
- [ ] Test: ohne Recht wird der Strom abgewiesen, bevor das erste Ereignis fließt
- [ ] Test: ein Lauf eines fremden Kunden liefert keinen Strom
- [ ] Test: der Rückfall auf Polling funktioniert — gefahren mit abgeschaltetem `EventSource`
- [ ] Der Herzschlag kommt an: eine Verbindung überlebt 60 s ohne Änderung
---
Quelle im Repo: `docs/issues/033-sse-live.md`
florian.wilde
added this to the v0.2.0 milestone 2026-10-04 16:19:17 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Ziel
Die Abarbeitungsansicht fragt alle zehn Sekunden nach dem Zustand. Das reicht für zwei Techniker an
einem Lauf, kostet aber bei zwanzig offenen Tabs zwanzig Abfragen je zehn Sekunden — und zehn
Sekunden sind lang, wenn zwei Leute dieselbe Liste abhaken.
Die Vorleistung steht: das Polling liegt in genau einem Route Handler
(
/api/laeufe/[id]/zustand) und lässt sich tauschen.Zu entscheiden
LISTEN/NOTIFYin Postgres als Signal — Empfehlung. Ein Trigger aufSchrittergebnisund
LaufschicktNOTIFY lauf_geaendert, '<laufId>'; der Route Handler hält eine Verbindungund schiebt weiter. Damit funktioniert es auch mit mehreren Instanzen der Anwendung, ohne
einen zweiten Dienst. Die Alternative — der Server fragt selbst alle zwei Sekunden — wäre
dasselbe Polling, nur an anderer Stelle.
auf eine gewöhnliche Anfrage. Wer kein
EventSourcehat oder hinter einem Proxy sitzt, derStröme puffert, bekommt den alten Weg. Ein Rückfall, der nie geprüft wird, ist keiner —
deshalb ein Test je Weg.
Aufgaben
NOTIFYin einer handgeschriebenen Migration, mit dem üblichen Vermerk (Prismakennt Trigger nicht und will sie löschen)
text/event-stream, schickt alle 25 s einen Kommentar als Herzschlag(sonst schließen Proxys die Verbindung) und beendet sauber bei
abortandere (CLAUDE.md: Route Handler deckt die Hülle nicht ab)
EventSourcemit Wiederverbindung; bei versteckter Lasche pausieren wie heute; beierrorauf Polling zurückfallen und das einmal melden, nicht bei jedem Versuchdocs/betrieb/reverse-proxy.md:proxy_buffering offfür nginx,flush_interval -1fürCaddy — ohne das kommt kein Ereignis an, und die Seite sieht aus, als sei sie hängen geblieben
Verbindungen nicht aufbraucht
Akzeptanzkriterien
Sekunde, ohne neu zu laden
EventSourceQuelle im Repo:
docs/issues/033-sse-live.md