Downloads from a remote cache used to run only as many at once as the run had workers, and uploads four at once. A transfer is a wait on a round trip, not work for a core. How vx now sizes each lane on its own, and why a missing key costs no request.
Caching
Caching
A task needs a core. A download from a remote cache mostly needs
patience: it sends a request and waits a round trip for the answer.
vx used to run downloads only as many at once as the run had workers,
and uploads four at once. On a 4-core runner talking to a cache 30 ms
away, that left the run waiting on round trips while the machine sat
idle.
Downloads, uploads and local restores now each have their own limit:
Lane
Default
Why
download
min(96, max(cores × 3, 64))
mostly waiting on the network
upload
min(96, max(cores × 3, 64)), per remote
the same, and runs in the background
restore
twice run.concurrency
local disk work, closer to a task
The formula for the network lanes is the one pnpm uses for its own
fetches: at least 64 at once, even on a small machine, and at most 96.
On a remote 30 ms away with 1,000 tasks, uploading every entry went
from 9.4 s to 3.8 s, and restoring every entry from the remote from
5.1 s to 2.6 s (min of 3 runs each).
Each limit is a setting, a flag and an env var, so a server that wants
fewer connections gets fewer:
Before a run starts its tasks, it asks each remote in one batch which of
its keys exist. A task that looked up its own key before that answer
came back used to send its own request anyway, even for a key the batch
was about to report missing. Now the task waits for the batch. A key the
remote does not have costs nothing beyond the one batch, and a key it
does have costs one download.
Plugins keep their own entries in the same cache, such as the run
timings @vzn/vx-schedule-history learns from. On a branch, a plugin
read used to ask the default branch’s entry and then the branch’s. Now
ctx.cache.get, has and hasMany make one request: the branch’s entry
by default, or the default branch’s when the plugin passes false.
vx used to decompress an archive on every local hit. Now the local cache keeps each entry as plain files, unpacked once, and a hit is a copy the kernel does. Why the archive stays only on the wire, why a hit checks nothing, and why vx never hard-links.
On a cold run every cache lookup misses, so vx no longer asks. The first task of a 1,090-package build now starts in about 230 ms, down from about 380 ms earlier the same day.
A fast run is mostly a run that skips work. Three jobs vx used to do on every run, and why each one went: housekeeping at exit, a walk of the shared store, and a "saved" figure that overstated what the cache saved.
Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.