Multi-Hoster-Upload/tasks/todo.md
Administrator e0f789f56f docs(tasks): high-concurrency lag audit — renderer measured-refuted, sync-fs was the blocker (v3.3.90)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 03:44:41 +02:00

45 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# High-concurrency lag audit (v3.3.90) — "lag ist immernoch da, ich vermute ab X gleichzeitig muss er alle Zeilen gebündelt updaten"
Method: 44-agent high-concurrency audit of the full upload→IPC→render path + Blink benchmark of the
renderer queue table (Playwright/Chromium = same Blink engine), targeting the user's NEW hypothesis:
"with ~100 concurrent uploads the renderer has to update ALL rows bundled rather than cleanly per-row."
## The user's hypothesis is MEASURED-REFUTED — the renderer is NOT the bottleneck.
Blink benchmark over scenarios Q=150..1000, M=10 active, 60 ticks each:
- renderQueueTable virtualizes at ≥200 rows; <200 = change-detecting in-place update.
- _updateRowInPlace is change-detecting (no forced reflow, no layout reads).
- median render <1 ms at Q=1000; only ~4/60 renders are full rebuilds even with progress-crossing sorts.
- progress is coalesced main-side (_progressByJob Map keyed by jobId + 100ms flush one batch sized by
active-job count, ~10/sec); renderer iterates the batch with cheap per-row handleProgress.
DOM amplification is ruled out by measurement. "Laggy at ~40% CPU / 8 cores" = ONE core at 100% =
main-thread saturation / synchronous blocking, not DOM.
## SHIPPED (v3.3.90) — the two real main-thread blockers, both behavior-preserving
1. lib/clouddrop-upload.js `_uploadChunked`: was reading each 16 MB chunk with `fs.readSync` SYNCHRONOUSLY
on the main event loop unique among the 5 uploaders (the other 4 stream async). Each read blocks the
WHOLE loop (~59 ms SSD, 30100 ms slow disk) freezes all progress/IPC/render/other-uploads, scaling
with the number of concurrent clouddrop uploads. Fits "laggy when uploading, worse with more concurrent."
User uses clouddrop. Fix: `fs.openSync`/`readSync`/`closeSync` `fs.promises.open` + `await fh.read` +
`await fh.close()`. Byte-equivalence verified by SHA-256 over all chunk-boundary cases (full chunk,
partial last chunk, 2/3/4-chunk, single byte) before shipping a chunk-read bug = corrupt upload.
2. lib/upload-manager.js rotation-retry (944) + suspect-alternate (1075) progressCb: both called
`_emitProgress` (a synchronous `emit('progress')` + fresh object spread) on EVERY stream chunk
(hundreds/sec per job) they were missing the 250 ms `lastEmitTime` gate that the primary path (631)
has. With many concurrent uploads in rotation/suspect mode that's real main-thread emit amplification.
Mirrored the gate exactly: activeEntry mutation stays UNGATED (stats/speed-monitor stay fresh), only the
emit is throttled to 4/sec. Behavior-preserving.
397/397 tests pass, eslint clean (1 pre-existing unrelated warning at line 554).
## DROPPED (advisor: measured fine, don't chase perception)
- Lowering the virtual-row threshold below 200: the Blink benchmark shows <200 in-place updates are already
sub-ms; no change warranted.
## OPEN — discriminator question to the user (do NOT declare victory blind)
With this user's real config (parallelCount 2 × 5 hosters 10 max concurrent), "100 concurrent" is only
reachable if the parallel counts were raised otherwise "100" is the QUEUE size and only ~10 upload at once.
Ask: (a) is the lag specifically during clouddrop uploads? (b) did you raise the per-hoster/global parallel
counts above 2? If it's true high concurrency (dozens of simultaneous undici streams funneling decode +
progress callbacks through the one main JS thread), that needs a concurrency cap or a worker/child process
NOT a micro-fix. The two shipped fixes are genuine improvements regardless; the answer decides whether a
bigger architectural change is the next step.