fix(renderer): prune session-stats sets at batch-done

_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.
This commit is contained in:
Administrator 2026-04-28 06:09:54 +02:00
parent 3ece93c363
commit 2c46430492
2 changed files with 18 additions and 5 deletions

View File

@ -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) {

View File

@ -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.