What concurrency limit should Gulp apply by default to prevent latency spikes during large file operations?
0 reputation · 19 Sept 2020, 07:27 UTC
0 reputation · 19 Sept 2020, 07:27 UTC
The goal is to understand whether Gulp’s core should automatically throttle parallel task execution to avoid latency spikes when many tasks perform heavy I/O, such as copying large files or running image optimizations.
Currently Gulp 4 relies on the orchestrator library and Node.js’s asynchronous I/O, which starts all tasks declared with gulp.parallel simultaneously. While this works well for lightweight streams, concurrent heavy‑file operations can saturate the disk and libuv thread pool, causing observable delays. The community has noted that built‑in throttling is absent, leaving users to adopt third‑party plugins or custom semaphores.
Should Gulp core provide a built‑in concurrency governor? If so, what default limit would balance throughput and latency? How could users override it per‑task or per‑build?
Gulp has no built-in concurrency governor, and there is no single correct default number. If Gulp core ever shipped one, the defensible default would be a small constant tied to the environment — the libuv thread pool size (4 by default) or the CPU count — because disk-bound work saturates I/O well before extra parallelism helps. Until then, throttling is a userland concern, and the practical ceiling you should design around is UV_THREADPOOL_SIZE.
One correction first: the question describes Gulp 4 as relying on "the orchestrator library." Orchestrator was Gulp 3.x. Gulp 4 and 5 schedule tasks through undertaker, which starts every task passed to gulp.parallel immediately. Neither version throttles task-level concurrency, and maintainers have historically kept scheduling minimal by design. Verify which you have:
node -e "console.log(require('gulp/package.json').dependencies)"The likely explanation for your latency spikes: Node's file I/O runs on the libuv thread pool, which defaults to 4 threads (configurable via UV_THREADPOOL_SIZE, max 1024). When many tasks each open large-file streams at once, operations queue on the pool and compete for the same disk bandwidth. Queued async work — including unrelated operations — waits longer, which surfaces as latency spikes rather than lost throughput.
Any specific number is a heuristic, not a measured optimum — the right value depends on SSD vs. HDD, file sizes, and whether tasks are I/O- or CPU-bound. That said:
UV_THREADPOOL_SIZE (or os.cpus().length as a fallback). Parallelism beyond the pool size mostly queues for file-heavy work.gulp.parallel or a task property such as task.maxConcurrency.Wrap heavy tasks in a small semaphore instead of waiting for core support:
const limit = 4;
let active = 0;
const queue = [];
function throttle(fn) {
return async (...args) => {
if (active >= limit) await new Promise(r => queue.push(r));
active++;
try { return await fn(...args); }
finally { active--; (queue.shift() || (() => {}))(); }
};
}Note that per-file concurrency inside a stream (plugin-level throttling) is a different knob from task-level concurrency — tune them separately.
Check your environment's ceiling and measure rather than guess:
node -e "console.log(process.env.UV_THREADPOOL_SIZE, require('os').cpus().length)"Run a representative build with concurrency capped at 1, 4, and unlimited, comparing wall time and worst-case task start latency. Raising UV_THREADPOOL_SIZE shifts the bottleneck to the disk itself, so treat it as a lever, not a fix. Also check current Gulp release notes before assuming no core option exists — this area could change in Gulp 5.x.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.