From 948ab84afedb444e16efb9d3577ada439ac1b060 Mon Sep 17 00:00:00 2001 From: Elton Turing Date: Mon, 7 Sep 2026 20:23:36 +0200 Subject: [PATCH] llmrouter: Slot an das Verbindungssignal binden (cancel feuert nicht) Gegenprobe am Produktivrouter nach dem ersten Fix: Ein abgebrochener Stream gab den Slot WEITERHIN nicht frei -- cancel() im TransformStream wird von Bun beim Verbindungsabbruch nicht aufgerufen. Immerhin griff die zweite Haerte: statt zwei Stunden Stille gab es nach 30 s ein 503 mit Grund. Jetzt haengt der Slot an req.signal, das beim Abbruch zuverlaessig feuert, plus einem Sicherheitsnetz von 660 s (laenger als der Upstream-Timeout darf kein Slot gehalten werden). Die Freigabe ist gegen Doppelaufruf gesichert, ein spaeteres flush() schadet also nicht. Co-Authored-By: Claude Opus 5 --- src/server.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/server.ts b/src/server.ts index a0abf33..c3b33c6 100644 --- a/src/server.ts +++ b/src/server.ts @@ -348,6 +348,16 @@ Bun.serve({ let freigeben: () => void; try { freigeben = await belegen(ziel.upstreamName, ziel.upstream); + // Der Slot haengt am LEBEN DER VERBINDUNG, nicht am Ende des Streams. + // Gegenprobe am 2026-09-07: cancel() im TransformStream feuert in Bun + // NICHT, wenn der Client abbricht -- der Slot blieb belegt, und erst die + // 30-s-Frist machte daraus ein 503 statt Stille (42i/intern#1283). + // req.signal feuert dagegen zuverlaessig; die Freigabe ist gegen + // Doppelaufruf gesichert, ein spaeteres flush() schadet also nicht. + // Dazu ein Sicherheitsnetz: laenger als der Upstream-Timeout darf kein + // Slot gehalten werden, egal was passiert. + req.signal.addEventListener("abort", () => freigeben(), { once: true }); + setTimeout(() => freigeben(), 660_000); } catch (e) { if (e instanceof SlotFrist) { eintrag.status = 503; eintrag.fehler = e.message; abschliessen();