DEV Community

Cover image for The cache on your terms
VX
VX

Posted on Edited on Originally published at vznjs.github.io

The cache on your terms

A cache is only useful when you can tell it what to do. vx gives you three
switches for a run, one store for every checkout, and a scope that keeps
untrusted runs from writing what everyone else reads.

Three switches

$ vx run build --all
  time      18ms · 1.27s saved

$ vx run build --all --force
  cache     4 miss
  time      658ms

$ vx run build --all --no-cache
  cache     4 no-cache
  time      638ms
Enter fullscreen mode Exit fullscreen mode

--force runs everything again and refreshes the cache with the result.
--no-cache neither reads nor writes. For finer control, --cache-policy takes
a read/write spec per layer:

$ vx run build --all --cache-policy=local:rw,remote:r   # read the remote, never upload
$ vx run build --all --cache-policy=local:r             # read local, write nothing local
$ vx run build --all --cache-policy=local:,remote:rw    # skip local, still upload
Enter fullscreen mode Exit fullscreen mode

VX_CACHE_POLICY sets the same spec for every run in a shell or CI job, and
cache.policy in .vx/workspace.ts sets the default; the flag beats the
variable, which beats the config. Whatever the spec, a lookup asks the
local cache first, then each remote in the order the plugins declare them.

// .vx/workspace.ts
import { defineWorkspace } from '@vzn/vx/config'

export default defineWorkspace({ cache: { policy: 'local:rw,remote:r' } })
Enter fullscreen mode Exit fullscreen mode

An empty spec is refused, never read as "everything on":

$ vx run build --all --cache-policy=
vx run: --cache-policy needs a spec like local:r, local:rw, remote:, or local:,remote:rw — pass --no-cache to disable every axis (see `vx run --help`)
Enter fullscreen mode Exit fullscreen mode

One store for every checkout

Artifacts live in ~/.vx/<id>/cache, where the id comes from the repo's
remote. Each checkout keeps only its own small index. So a second clone
or a fresh git worktree hits what the first one built:

$ git worktree add ../demo-wt && cd ../demo-wt
$ vx run build --all
  cache     ▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰▰
            4 local
  time      22ms · 1.24s saved
Enter fullscreen mode Exit fullscreen mode

The new worktree had no dist. All four builds came back from the shared
store in 22ms. To put the cache somewhere else, use --cache-dir,
VX_CACHE_DIR or cache.dir in .vx/workspace.ts.

Who may write the remote cache

A remote cache is shared, so a write is a promise to everyone who reads
it. cache.scope decides where a run's writes land:

diagram

  • With no scope, a run reads and writes the default keys.
  • A scope, like pr-123, reads the default keys first, then its own, and writes only its own. A pull request never writes what main reads.
  • Whether a run writes at all is the policy's call. Off CI the default reads the remote and writes nothing.
$ vx run build --cache-scope pr-123
Enter fullscreen mode Exit fullscreen mode

--cache-scope beats VX_CACHE_SCOPE, which beats cache.scope in
.vx/workspace.ts. On GitHub Actions, the
@vzn/vx-ci plugin
sets the scope for you: pr-<n> on a pull request, none on the
default branch. The scope is a client-side rule, so a cache server that
checks tokens is still the real boundary.

Every flag is in the CLI reference, and cache is in
the schema.


Originally published on the vx blog. vx is open-source (MIT) task orchestration with a build cache: vznjs.github.io/vx · GitHub.

Written with AI assistance.

Top comments (0)