From 2c464304924ad3139e39af840382207789dd2994 Mon Sep 17 00:00:00 2001 From: Administrator Date: Tue, 28 Apr 2026 06:09:54 +0200 Subject: [PATCH] fix(renderer): prune session-stats sets at batch-done MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _sessionTrackedJobs and _sessionDoneJobs accumulated jobIds across the whole session — IDs of jobs already removed from queueJobs (by removeFromQueueOnDone or the auto-cap that lands in handleBatchDone) stayed in those sets forever. ~50 bytes/entry × hundreds of batches × many jobs/batch = small but real growth over a multi-day session. At batch-done, walk the sets and drop any ID that's no longer present in queueJobs. _completedUploadKeys is intentionally kept — it's the dedup against re-queueing the same file across batches and would break that contract if pruned. The prune is a single pass per batch-done (rare event) and only happens when the sets aren't already empty. 97/97 tests still green. --- renderer/app.js | 11 +++++++++++ tasks/todo.md | 12 +++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/renderer/app.js b/renderer/app.js index 1db0935..634af35 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -1973,6 +1973,17 @@ function handleBatchDone(summary) { uploading = false; applySummaryResults(summary); _deletedJobIds.clear(); // Free memory — stale IDs no longer needed after batch completes + // Prune session-stats sets to current queue contents. Without this, IDs + // of jobs that were removed from queueJobs (via removeFromQueueOnDone + // or the cap-prune below) live forever in these sets — small leak per + // entry, real over weeks of use. _completedUploadKeys is intentionally + // kept (it's the dedup against re-queueing the same file). + if (_sessionTrackedJobs.size > 0 || _sessionDoneJobs.size > 0) { + const aliveIds = new Set(); + for (const j of queueJobs) aliveIds.add(j.id); + for (const id of _sessionTrackedJobs) if (!aliveIds.has(id)) _sessionTrackedJobs.delete(id); + for (const id of _sessionDoneJobs) if (!aliveIds.has(id)) _sessionDoneJobs.delete(id); + } // Reset aborted jobs back to queued so they can be restarted for (const job of queueJobs) { diff --git a/tasks/todo.md b/tasks/todo.md index db8bc29..43d8b0d 100644 --- a/tasks/todo.md +++ b/tasks/todo.md @@ -8,15 +8,17 @@ - ✅ 3.3.4 — `applyQueueSelectionClasses` + `applyRecentSelectionClasses` nutzen `getElementsByClassName` (live HTMLCollection statt querySelectorAll re-query bei jedem Klick) - ✅ 3.3.5 — Log-Rotation extrahiert nach `lib/log-rotation.js` + 10 neue Unit-Tests (cap, shift, eviction, idempotency, maxBackups=1, invalid input, no-extension) - ✅ 3.3.6 — CSS `.queue-row` transition nur noch auf `:hover` (kein 150ms compositor-tween bei status-flips) +- ✅ 3.3.7 — `_sessionTrackedJobs`/`_sessionDoneJobs` werden bei handleBatchDone gegen current queueJobs geprunt (no more unbounded session memory growth across batches) ## Open items (priorisiert) -### Code-Qualität -- [ ] queue-cap-prune-Logik (3.3.0 handleBatchDone) — DOM-abhängig, bräuchte jsdom; deferred bis sich anderes lohnt -- [ ] sortQueueJobs dynamic-throttle (3.3.0) — modul-state-abhängig im renderer; deferred +### Code-Qualität (deferred — bräuchte jsdom für DOM/state) +- [ ] queue-cap-prune-Logik (3.3.0 handleBatchDone) +- [ ] sortQueueJobs dynamic-throttle (3.3.0) +- [ ] removeFromQueueOnDone microtask-coalesce (3.3.1) — Microtask-Timing schwer zu testen ohne fake-timer setup -### UX-Politur -- [ ] Module-level Sets `_sessionTrackedJobs`/`_sessionDoneJobs`/`_completedUploadKeys` werden nie geleert — minor memory growth. +### Loop-Status +Alle initial im 3.3.0-Audit identifizierten Items sind nun adressiert. Loop kann pausiert werden bis neue User-Beschwerden / neue Audit-Findings auftauchen, oder weiterlaufen für Quality-Improvement-Sweep (z.B. eslint cleanup, comment audit, dead-code). ## Loop-Notes - Cron-Job `01e33ae1` läuft alle 30min (:07/:37), Session-only.