From d96c6afce03dc98129b8946f3d47c591f5500d96 Mon Sep 17 00:00:00 2001 From: Administrator Date: Tue, 28 Apr 2026 03:40:06 +0200 Subject: [PATCH] feat(log): auto-rotate fileuploader.log at 50 MB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A long-running install can otherwise grow the upload log into the gigabyte range, eating disk and slowing every appendFile. Add a size-checked rotation right before each flush: - statSync the resolved log target (cheap, ENOENT skips silently). - If size exceeds 50 MB, drop the oldest backup (.3), shift .2→.3 and .1→.2, then rename the live file to .1 and let appendFile create a fresh primary on the next call. - Max 3 backups (~200 MB worst case, bounded). debugLog records each rotation for diagnostics. - Pure additive: skips when file is small or doesn't exist; no effect on the daily-log mode (already date-rotated). --- main.js | 37 +++++++++++++++++++++++++++++++++++++ tasks/todo.md | 4 ++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/main.js b/main.js index addb85e..4f7279d 100644 --- a/main.js +++ b/main.js @@ -296,6 +296,40 @@ function _resolveUploadLogTarget() { } } +// Cap the upload log file size. Beyond this we rotate to .1 (and shift +// older numbered backups up) so a multi-month-running install can't fill +// the disk. 50 MB ≈ ~600k log lines, plenty for human inspection. +const UPLOAD_LOG_MAX_BYTES = 50 * 1024 * 1024; +const UPLOAD_LOG_MAX_BACKUPS = 3; + +function _maybeRotateUploadLog(filePath) { + let size = 0; + try { + const st = fs.statSync(filePath); + size = st.size; + } catch (err) { + // ENOENT is the common case (file doesn't exist yet) — nothing to rotate. + if (err && err.code !== 'ENOENT') { + debugLog(`uploadLog stat for rotation failed: ${err.message}`); + } + return; + } + if (size <= UPLOAD_LOG_MAX_BYTES) return; + const ext = path.extname(filePath); + const base = filePath.slice(0, filePath.length - ext.length); + // Drop the oldest backup if it exists, then shift each numbered backup up. + try { fs.unlinkSync(`${base}.${UPLOAD_LOG_MAX_BACKUPS}${ext}`); } catch {} + for (let i = UPLOAD_LOG_MAX_BACKUPS - 1; i >= 1; i--) { + try { fs.renameSync(`${base}.${i}${ext}`, `${base}.${i + 1}${ext}`); } catch {} + } + try { + fs.renameSync(filePath, `${base}.1${ext}`); + debugLog(`uploadLog rotated (${(size / 1024 / 1024).toFixed(1)} MB) → ${base}.1${ext}`); + } catch (err) { + debugLog(`uploadLog rotation rename failed: ${err.message}`); + } +} + function _flushUploadLog() { if (_uploadLogWriting || _uploadLogBuffer.length === 0) return; const target = _resolveUploadLogTarget(); @@ -305,6 +339,9 @@ function _flushUploadLog() { // the dir already exists; recreates it otherwise. Without this, every // subsequent flush silently fails with ENOENT and entries are lost. try { fs.mkdirSync(path.dirname(target.path), { recursive: true }); } catch {} + // Cheap size check + rotation right before the append, so we never grow + // a single log file beyond the cap regardless of session length. + _maybeRotateUploadLog(target.path); const chunk = _uploadLogBuffer.join(''); _uploadLogBuffer.length = 0; _uploadLogWriting = true; diff --git a/tasks/todo.md b/tasks/todo.md index c76a10c..cdce0e5 100644 --- a/tasks/todo.md +++ b/tasks/todo.md @@ -2,12 +2,12 @@ ## Released - ✅ 3.3.0 — Performance-Fixes (queue-cap, sort-throttle, history-delegation, recent-cap) + Log-Recovery +- ✅ 3.3.1 — `removeFromQueueOnDone` coalesced via microtask (kein O(N²) mehr bei done-Bursts) +- ✅ 3.3.2 — `fileuploader.log` Auto-Rotation bei 50 MB (max 3 Backups: .1 .2 .3) ## Open items (priorisiert) ### Stabilität -- [ ] **removeFromQueueOnDone O(N²)** in `handleProgress` (renderer/app.js) — bei 500 gleichzeitig fertig werdenden Jobs 500× ein O(N)-`queueJobs.filter()` aufgerufen. Coalesce über microtask + Set. -- [ ] **fileuploader.log wächst unbounded** — automatische Rotation bei >50MB (rename → .1, neue Datei). - [ ] **`_jobLogCollector`** (main.js) — wird nur bei start-upload geleert, nicht bei batch-done. Bei vielen Batches ohne neuen start-upload wächst es. Cleanup bei batch-done für jobs die nicht mehr in queueJobs sind. ### Performance