fix(upload): re-check cancellation after _sleep in rotation while-loop

The account-rotation while-loop entered with a signal.aborted /
stopAfterActive check (line 681) but then awaited _sleep(800) on
line 690 (waiting for main.js to resolve the next fallback) without
re-checking on the way out. If the user cancelled during that 800 ms
window the loop kept going — resolved the override, set up new
credentials, fired retrying-event, started a fresh attempt loop —
before _executeUpload's own signal handling finally noticed the
abort. Cancellation latency could therefore stretch by an extra
attempt's worth of work per still-spinning hoster.

One-line fix: add the same `if (signal.aborted || this.stopAfterActive)
break` after the await. Found by deep-audit MED-5.

126/126 tests still green; the fix is a guard on an already-tested
flow, no test infrastructure exists for cancel-during-rotation
specifically (would need fake-timer + mocked override-resolution).
This commit is contained in:
Administrator 2026-04-28 10:39:09 +02:00
parent 7267adfd03
commit 10ae46c44d
2 changed files with 7 additions and 2 deletions

View File

@ -688,6 +688,11 @@ class UploadManager extends EventEmitter {
}); });
this.emit('account-failed', { hoster: task.hoster, accountId: task.accountId }); this.emit('account-failed', { hoster: task.hoster, accountId: task.accountId });
await this._sleep(800, signal); await this._sleep(800, signal);
// Re-check after the await: the user could have cancelled while
// we were waiting for main.js to resolve the fallback. Without
// this, rotation proceeds another full attempt-loop's worth of
// work before the next signal-check inside _executeUpload notices.
if (signal.aborted || this.stopAfterActive) break;
} else { } else {
this._rotLog('already-marked', { this._rotLog('already-marked', {
jobId, hoster: task.hoster, fileName, accountId: task.accountId jobId, hoster: task.hoster, fileName, accountId: task.accountId

View File

@ -16,11 +16,11 @@
- ✅ 3.3.12 — Race condition fix: `uploadManager = null` in batch-done clobberte einen frisch gespawnten Manager wenn user mid-await neuen batch startete (deep-audit finding HIGH-1) - ✅ 3.3.12 — Race condition fix: `uploadManager = null` in batch-done clobberte einen frisch gespawnten Manager wenn user mid-await neuen batch startete (deep-audit finding HIGH-1)
- ✅ 3.3.13 — `save-global-settings-sync` reportet jetzt `returnValue=false` bei Fehlern + debugLog statt silent swallow; TOCTOU bei .bak-Refresh in beiden Pfaden (main.js + lib/config-store.js _atomicWrite) entkoppelt: bak-Read-Failure failt nicht mehr den ganzen Save (deep-audit findings HIGH-2 + MED-4) - ✅ 3.3.13 — `save-global-settings-sync` reportet jetzt `returnValue=false` bei Fehlern + debugLog statt silent swallow; TOCTOU bei .bak-Refresh in beiden Pfaden (main.js + lib/config-store.js _atomicWrite) entkoppelt: bak-Read-Failure failt nicht mehr den ganzen Save (deep-audit findings HIGH-2 + MED-4)
- ✅ 3.3.14 — Parser-null-payload guard: `uploadFile` normalisiert payload zu `{}` falls `JSON.parse('null')` o.ä.; `parseDoodstreamResult` + `parseByseResult` haben defensive guards für direct callers + 7 neue Unit-Tests (null/non-object, malformed entries, fileRejected/accountError flips, valid filecode happy path) - ✅ 3.3.14 — Parser-null-payload guard: `uploadFile` normalisiert payload zu `{}` falls `JSON.parse('null')` o.ä.; `parseDoodstreamResult` + `parseByseResult` haben defensive guards für direct callers + 7 neue Unit-Tests (null/non-object, malformed entries, fileRejected/accountError flips, valid filecode happy path)
- ✅ 3.3.15 — Cancellation latency fix: nach `_sleep(800)` in der rotation-while-loop wird `signal.aborted`/`stopAfterActive` re-checkt bevor das ganze override-resolution-Setup läuft (deep-audit MED-5)
## Open items (priorisiert) ## Open items (priorisiert)
### Stabilität (neu aus deep-audit) (alle stabilitäts-items aus deep-audit erledigt)
- [ ] Cancellation latency in retry-loop's account-rotation block (lib/upload-manager.js:680-792) — re-check signal.aborted after each await.
### Code-Qualität (deferred) ### Code-Qualität (deferred)
- [ ] removeFromQueueOnDone microtask-coalesce (3.3.1) — Microtask-Timing schwer zu testen ohne fake-timer setup - [ ] removeFromQueueOnDone microtask-coalesce (3.3.1) — Microtask-Timing schwer zu testen ohne fake-timer setup