From 4432fa25e8425b9481c1e72277d77d407380b741 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Mon, 8 Jun 2026 22:33:11 +0200 Subject: [PATCH] Fix: Logger-Flush konnte ungeschriebene Zeilen verlieren (Race mit 1MB-Cap) flushAsync nahm eine Kopie der pending-Zeilen und entfernte sie nach dem await per Index-Zaehlung (slice(snapshot.length)). Feuerte waehrend des awaits ein write() den 1MB-Buffer-Cap, der vorne Zeilen wegshiftet, war die Zaehlung desynchron und verwarf neu hinzugekommene, noch nicht geschriebene Zeilen. Jetzt: pending-Zeilen per Move uebernehmen (Buffer auf [] zuruecksetzen) statt kopieren; await-Zeit-writes laufen in einen frischen Buffer. Bei Schreibfehler werden die Zeilen wieder vorn eingereiht und der Cap erneut angewandt. --- src/main/logger.ts | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/main/logger.ts b/src/main/logger.ts index 76b3078..6bcf67a 100644 --- a/src/main/logger.ts +++ b/src/main/logger.ts @@ -183,7 +183,14 @@ async function flushAsync(): Promise { } flushInFlight = true; - const linesSnapshot = pendingLines.slice(); + // Move (not copy) the pending lines out and take ownership. A concurrent write() + // during the await below pushes new lines AND can trim the 1MB cap from the FRONT + // of pendingLines; the old count-based removal (pendingLines.slice(snapshot.length)) + // then sliced off the wrong lines and dropped unwritten ones. Resetting the buffer + // here means await-time writes queue independently and nothing desyncs. + const linesSnapshot = pendingLines; + pendingLines = []; + pendingChars = 0; const chunk = linesSnapshot.join(""); try { @@ -200,9 +207,19 @@ async function flushAsync(): Promise { } else if (!primary.ok) { writeStderr(`LOGGER write failed: ${primary.errorText}\n`); } - if (wroteAny) { - pendingLines = pendingLines.slice(linesSnapshot.length); - pendingChars = Math.max(0, pendingChars - chunk.length); + if (!wroteAny) { + // Write failed: requeue the unwritten lines AHEAD of anything that arrived + // during the await (preserve order), then re-apply the buffer cap so a + // persistent write failure cannot grow the buffer without bound. + pendingLines = linesSnapshot.concat(pendingLines); + pendingChars += chunk.length; + while (pendingChars > LOG_BUFFER_LIMIT_CHARS && pendingLines.length > 1) { + const removed = pendingLines.shift(); + if (!removed) { + break; + } + pendingChars = Math.max(0, pendingChars - removed.length); + } } } finally { flushInFlight = false;