diff --git a/frontend/src/lib/api/endpoints/deltaUpload.ts b/frontend/src/lib/api/endpoints/deltaUpload.ts index af5b5a48..01006653 100644 --- a/frontend/src/lib/api/endpoints/deltaUpload.ts +++ b/frontend/src/lib/api/endpoints/deltaUpload.ts @@ -11,9 +11,22 @@ import { getCsrfToken } from '$lib/api/csrf'; import { createFileByHash, dedupCheckBatch } from '$lib/api/endpoints/files'; import { blake3HexOfFile } from '$lib/vendor/hashWasm'; -/** Files smaller than this skip delta: the round-trips cost more than the bytes. */ +/** Files smaller than this skip delta: the round-trips cost more than the bytes. + * Also the upper bound for client-side whole-file hashing (instant by-hash + * uploads) — we never read a file larger than this fully into memory. */ export const DELTA_UPLOAD_MIN_SIZE = 8 * 1024 * 1024; +/** Only files at least this large actually run the delta worker. A delta worker + * opens SEVERAL concurrent requests (overlapping `negotiate` batches + chunk + * PUTs); a few running at once exhaust the browser's ~6 connections-per-host + * budget and starve plain uploads (they queue, then the upload watchdog cancels + * them — the "stuck at N%" folder upload). Typical large files (e.g. tens of MB) + * therefore go through a single-connection plain upload; delta is reserved for + * genuinely huge files, where chunked, resumable transfer earns its keep and few + * run concurrently. (Delta's real payoff — sub-file dedup — only helps on + * re-upload anyway, not the first upload that dominates these batches.) */ +const DELTA_WORKER_MIN_SIZE = 64 * 1024 * 1024; + const DELTA_WORKER_URL = '/workers/deltaWorker.js'; const DELTA_TIMEOUT_BASE_MS = 120_000; const DELTA_TIMEOUT_PER_GB_MS = 90_000; @@ -59,7 +72,7 @@ export function tryDeltaUpload( ): Promise { if ( !folderId || - file.size < DELTA_UPLOAD_MIN_SIZE || + file.size < DELTA_WORKER_MIN_SIZE || usable === false || typeof Worker === 'undefined' ) { diff --git a/frontend/src/routes/files/[...path]/+page.svelte b/frontend/src/routes/files/[...path]/+page.svelte index 169bca9a..a310de11 100644 --- a/frontend/src/routes/files/[...path]/+page.svelte +++ b/frontend/src/routes/files/[...path]/+page.svelte @@ -289,11 +289,12 @@ } } - // Upload at most this many files concurrently. Bounded so one stuck file - // blocks only its own lane (the others keep going) without overwhelming the - // browser's per-host connection cap, spawning too many delta workers, or - // over-contending the server with many large concurrent uploads. - const UPLOAD_CONCURRENCY = 3; + // Upload at most this many files concurrently. Kept low so we stay well under + // the browser's ~6 connections-per-host budget — leaving headroom for the + // session-refresh/poll requests and (for genuinely huge files) a delta worker, + // which itself opens several connections. Over-subscribing here is what made + // small uploads queue until the watchdog cancelled them ("stuck at N%"). + const UPLOAD_CONCURRENCY = 2; /** Outer backstop deadline (ms). The plain-upload path already self-aborts on * a stalled connection (see `uploadFileWithProgress`); this only catches a