Ein klingelndes Telefon haengt nicht mehr an einem einzigen Kanal
Filipe: "wenn vanvan rangeht und redet klingelt es immer noch bei mir
weiter, der anruf verbindet nicht richtig."
=== WAS DAS PROTOKOLL SAGT ===
17:04:52 anruf_start Person 1 (Filipe)
17:05:25 anruf_ende Person 4 (VanVan) 24 s
17:05:31 anruf_ende Person 1 (Filipe) 39 s
Sie WAR im Gespraech -- der Server hat sie 24 Sekunden als
Teilnehmerin gefuehrt. Das Ereignis "dabei" ist also verschickt
worden. Bei Filipe kam es nicht an: `tonAus()` ist das Erste im
`dabei`-Zweig, noch vor jeder Pruefung, und das Tuten lief weiter.
=== GEPRUEFT UND AUSGESCHLOSSEN ===
Raumzugehoerigkeit beide in Raum 1, bei keinem `raus_am` gesetzt
Verkabelung Server sendet mit `art: "anruf"`, chat.js reicht
an window.anrufEreignis weiter, anruf.js nimmt
entgegen -- alle drei Stellen stimmen
Tonsteuerung ein einziger Taktgeber, `tonAus` raeumt ihn;
kein zweiter Weg, der ihn neu startet
Ereignisstrom Keep-alive vorhanden, Kopfzeilen richtig
(no-transform, X-Accel-Buffering: no)
Service Worker hat gar keinen fetch-Handler, kann also kein
altes Skript ausliefern
teilnehmerVon vs.
teilnehmerFuerAnruf reicht nur durch, dieselbe Abfrage
Es geht unterwegs verloren, auf einem Weg, der von hier aus nicht
messbar ist: Ereignisstrom ueber Cloudflare, ein schlafender Reiter,
ein Neustart im falschen Moment.
=== ALSO NICHT WEITERSUCHEN, SONDERN DIE ABHAENGIGKEIT BESEITIGEN ===
Ein klingelndes Telefon darf nicht an einem einzigen, zerbrechlichen
Kanal haengen. Solange es klingelt, fragt der Anrufer jetzt SELBST
nach: "ist schon jemand dran?" -- alle zwei Sekunden an
`/workspace/api/anruf/:raum`, das es laengst gibt.
Der Ereignisstrom bleibt der erste Weg, er ist schneller. Das hier ist
das Netz darunter. Kommt das Ereignis an, hat die Nachfrage nichts
mehr zu tun und haelt von selbst an (sie prueft `anruf.beginn` und die
bekannten Teilnehmer).
Sie hoert an JEDEM Ende auf: beim Auflegen, wenn die Verbindung steht,
wenn das Ereignis doch ankommt, wenn der Anruf vorbei ist. Eine
Schleife, die weiterlaeuft, fragt sonst auf jedem Geraet, das je
telefoniert hat, alle zwei Sekunden nach einem Anruf, den es nicht
mehr gibt.
Alle zwei Sekunden und nicht jede halbe: Es klingelt hoechstens zwei
Minuten, das sind sechzig Abrufe.
=== ZWEI DINGE, DIE DIESE SUCHE ERST SO MUEHSAM GEMACHT HABEN ===
DAS PROTOKOLL KANNTE ANFANG UND ENDE, ABER NICHT DEN MOMENT DAZWISCHEN.
Die wichtigste Frage -- "ist sie ueberhaupt rangegangen?" -- war nur
ueber einen Umweg zu beantworten (ein `anruf_ende` mit ihrer Nummer).
Das ist eine Schlussfolgerung, keine Auskunft. `anruf_dabei` steht
jetzt drin, mit der Zahl der Beteiligten.
UND EINE PRUEFUNG WAR GRUEN, OHNE ETWAS ZU PRUEFEN. In chatEreignis
stand `(zuschauer.get(personId) || []).length` -- `zuschauer` haelt
aber Mengen, und eine Menge hat kein `length`. Der Ausdruck war IMMER
undefined, also immer falsch, also wurde nie uebersprungen: Wer die
Seite offen hatte, bekam zusaetzlich zur Nachricht auf dem Bildschirm
noch eine Meldung aufs Handy.
Der Kommentar drei Zeilen darueber warnt woertlich davor ("der
schnellste Weg, dass er Benachrichtigungen abschaltet"), und
`siehtZu()` weiter unten macht es mit `.size` richtig. Die Absicht
stand da, die Zeile tat das Gegenteil.
Meine eigene Pruefung hat das mitgetragen: Sie bestaetigte den alten
WORTLAUT statt sein VERHALTEN und war deshalb gruen. Genau die
Hausregel vom 01.09. -- ein gruener Haken sagt nur, dass die Bedingung
erfuellt war, nicht dass sie das Richtige geprueft hat. Jetzt prueft
sie auf `.size`.
GEMESSEN: pruef-anruf-klingelt 24/0 (vorher 17), pruef-anruf 114/0,
pruef-turn-wege 15/0, pruef-meldungen 8/0.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -271,6 +271,7 @@
|
||||
waere das, was man an einer Telefonanlage hasst. */
|
||||
tonAus();
|
||||
klingelWache(); // niemand-geht-ran-Wecker abbestellen
|
||||
nachfrageStoppen(); // und die Nachfrage braucht es nicht mehr
|
||||
if (anruf && !anruf.beginn) anruf.beginn = Date.now();
|
||||
/* Der Puls hoert auf zu atmen, sobald die Verbindung steht --
|
||||
eine Bewegung, die nie aufhoert, wird nach zwei Minuten zur
|
||||
@@ -434,6 +435,8 @@
|
||||
und schickt den Zeitpunkt sogar mit (`laeuft_bis`) -- benutzt
|
||||
hat ihn nie jemand. */
|
||||
klingelWache(KLINGELT_MS);
|
||||
/* Und der zweite Weg, falls das Ereignis nicht ankommt. */
|
||||
nachfrageStarten(raumId);
|
||||
|
||||
const a = await fetch(`/workspace/api/anruf/${raumId}/start`, {
|
||||
method: 'POST', credentials: 'same-origin',
|
||||
@@ -505,6 +508,7 @@
|
||||
async function auflegen(melden) {
|
||||
tonAus();
|
||||
klingelWache();
|
||||
nachfrageStoppen();
|
||||
$('anruf')?.removeAttribute('data-steht');
|
||||
klingelnAus();
|
||||
if (!anruf) { const k = $('anruf'); if (k) k.hidden = true; return; }
|
||||
@@ -825,6 +829,90 @@
|
||||
const KLINGELT_MS = 122000;
|
||||
let klingelWecker = null;
|
||||
|
||||
/* =====================================================================
|
||||
DER ZWEITE WEG: SELBST NACHFRAGEN — 19.09.2026
|
||||
|
||||
Filipe: „wenn vanvan rangeht und redet klingelt es immer noch bei
|
||||
mir weiter, der anruf verbindet nicht richtig."
|
||||
|
||||
NACHGEMESSEN IM PROTOKOLL DES SERVERS:
|
||||
|
||||
17:04:52 anruf_start Person 1 (Filipe)
|
||||
17:05:25 anruf_ende Person 4 (VanVan) 24 s
|
||||
17:05:31 anruf_ende Person 1 (Filipe) 39 s
|
||||
|
||||
Sie WAR im Gespräch -- der Server hat sie 24 Sekunden lang als
|
||||
Teilnehmerin geführt. Das Ereignis „dabei" ist also verschickt
|
||||
worden. Bei Filipe kam es nicht an: `tonAus()` ist das Erste im
|
||||
`dabei`-Zweig, noch vor jeder Prüfung, und das Tuten lief weiter.
|
||||
|
||||
Geprüft und ausgeschlossen: Raumzugehörigkeit (beide drin, niemand
|
||||
`raus_am`), die Ereignis-Verkabelung (Server sendet mit der
|
||||
richtigen Art, chat.js reicht weiter, anruf.js nimmt entgegen),
|
||||
die Tonsteuerung (ein einziger Taktgeber, `tonAus` räumt ihn),
|
||||
ein zwischenspeichernder Service Worker (es gibt keinen).
|
||||
|
||||
Es geht also unterwegs verloren -- auf einem Weg, den ich von hier
|
||||
aus nicht messen kann: Ereignisstrom über Cloudflare, ein
|
||||
schlafender Reiter, ein Neustart des Servers im falschen Moment.
|
||||
|
||||
DARUM WIRD DIE URSACHE NICHT WEITER GESUCHT, SONDERN DIE
|
||||
ABHÄNGIGKEIT BESEITIGT. Ein klingelndes Telefon darf nicht an
|
||||
einem einzigen, zerbrechlichen Kanal hängen. Solange es klingelt,
|
||||
fragt der Anrufer jetzt SELBST nach: „ist schon jemand dran?"
|
||||
|
||||
Das ist kein Ersatz für den Ereignisstrom -- der ist schneller und
|
||||
bleibt der erste Weg. Es ist das Netz darunter. Kommt das Ereignis
|
||||
an, hat die Nachfrage nichts mehr zu tun (sie prüft `anruf.beginn`
|
||||
und die bekannten Teilnehmer und hält von selbst an).
|
||||
|
||||
ALLE ZWEI SEKUNDEN, NICHT JEDE HALBE: Es klingelt höchstens zwei
|
||||
Minuten, das sind sechzig Abrufe. Häufiger zu fragen kostet Akku
|
||||
für eine Sekunde, die niemand merkt. */
|
||||
let nachfrage = null;
|
||||
|
||||
function nachfrageStarten(raumId) {
|
||||
nachfrageStoppen();
|
||||
nachfrage = setInterval(async () => {
|
||||
if (!anruf || anruf.raumId !== raumId) return nachfrageStoppen();
|
||||
/* Steht die Verbindung schon, gibt es nichts nachzufragen. */
|
||||
if (anruf.beginn) return nachfrageStoppen();
|
||||
let d;
|
||||
try {
|
||||
const a = await fetch(`/workspace/api/anruf/${raumId}`,
|
||||
{ credentials: 'same-origin' });
|
||||
if (!a.ok) return;
|
||||
d = await a.json();
|
||||
} catch { return; } /* ein Aussetzer ist kein Ergebnis */
|
||||
if (!anruf || anruf.raumId !== raumId) return nachfrageStoppen();
|
||||
|
||||
/* Der Anruf ist vorbei, ohne dass wir es erfahren haben. */
|
||||
if (!d.laeuft) { nachfrageStoppen(); tonAus(); lage('Der Anruf ist vorbei.');
|
||||
setTimeout(() => { if (anruf) auflegen(); }, 1500); return; }
|
||||
|
||||
const andere = (d.wer || []).filter((w) => w.id !== ichId);
|
||||
if (!andere.length) return; /* es klingelt weiter, alles richtig */
|
||||
|
||||
/* JEMAND IST DRAN. Ab hier dasselbe wie beim Ereignis „dabei" --
|
||||
und es schadet nicht, wenn beides passiert: `verbindungFuer`
|
||||
gibt eine bestehende Verbindung zurück, und ein zweites
|
||||
Angebot an dieselbe Gegenstelle entsteht gar nicht erst. */
|
||||
nachfrageStoppen();
|
||||
tonAus();
|
||||
klingelWache();
|
||||
lage('Baut auf …');
|
||||
for (const w of andere) {
|
||||
if (anruf.verbindungen.has(w.id)) continue;
|
||||
if (ichId < w.id) angebotMachen(w.id);
|
||||
else verbindungFuer(w.id);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function nachfrageStoppen() {
|
||||
if (nachfrage) { clearInterval(nachfrage); nachfrage = null; }
|
||||
}
|
||||
|
||||
/** Ohne Zahl: abbestellen. Mit Zahl: nach so vielen Millisekunden
|
||||
* aufgeben, falls bis dahin niemand rangegangen ist. */
|
||||
function klingelWache(ms) {
|
||||
@@ -861,6 +949,7 @@
|
||||
/* Jemand ist rangegangen -- auch wenn die Verbindung noch ein
|
||||
paar Sekunden braucht, klingelt hier nichts mehr. */
|
||||
tonAus();
|
||||
nachfrageStoppen();
|
||||
if (!anruf || d.raum_id !== anruf.raumId) return;
|
||||
if (d.person?.id === ichId) return;
|
||||
/* NICHT „Verbunden" MELDEN, SOLANGE NICHTS VERBUNDEN IST.
|
||||
|
||||
Reference in New Issue
Block a user