Skip to content

Repository files navigation

gocachez

gocachez is a GOCACHEPROG helper for the Go build cache. It stores cache artifacts as zstd-compressed files, materializes uncompressed files for the Go command on demand, and evicts old compressed entries automatically.

This project was inspired by the discussion on golang/go#76337.

Go std:

Mode Cold time Warm time Disk usage
No GOCACHEPROG 36.31s 4.06s 1.60GB
gocachez 40.97s 5.65s 403MB
+4.66s (+12.8%) +1.59s (+39.1%) -74.8%

typescript-go:

Mode Cold time Warm time Disk usage
No GOCACHEPROG 89.20s 4.76s 4.95GB
gocachez 96.84s 6.97s 453MB
+7.64s (+8.6%) +2.21s (+46.4%) -90.9%

Installation

Requires Go 1.25 or newer.

$ go install github.com/jakebailey/gocachez@latest
$ go env -w GOCACHEPROG=gocachez

Undo:

$ go env -u GOCACHEPROG

Commands

Show help:

$ gocachez -h

Run as a GOCACHEPROG helper:

$ gocachez

Remove inactive cache state:

$ gocachez clean

clean removes blobs, live files, and catalog state that no active gocachez process is using. State for active live runs is preserved.

Show cache state:

$ gocachez status

status reports the current cache configuration and state.

How it works

gocachez implements the Go command's external cache protocol over stdin and stdout. The Go command sends put requests when it wants to store an artifact and get requests when it wants to retrieve one.

On put, gocachez:

  1. streams the artifact body into a zstd encoder,
  2. writes the compressed bytes into blobs/,
  3. writes the uncompressed body into the current run's live/ directory, and
  4. returns that live file path to the Go command as DiskPath.

The compressed blob is the durable cache entry. The live file is only the temporary uncompressed file the Go command needs in order to read the artifact from disk.

On get, gocachez looks up the action ID in SQLite. On a hit, it opens the compressed blob, decompresses it into the current run's live/ directory, and returns the materialized path as DiskPath.

Live files are removed when the GOCACHEPROG process closes. If gocachez exits abnormally, a later run can safely clean up abandoned live files: each run holds an OS file lock, and another process only reclaims a run directory after it can acquire that lock.

Some live files can escape the Go command process through go list output. For example, go list -export reports an Export path that tools such as go/packages and golangci-lint may open after the Go command has closed its GOCACHEPROG helper. go list -compiled can also report generated cgo source paths in CompiledGoFiles, and go list -test can report a generated _testmain.go path in the synthetic test main package's GoFiles.

To support those tools without keeping large uncompressed archives around, gocachez treats these escaped files specially on close. Package archives are replaced with small archives containing only their __.PKGDEF export data, and generated Go source files that can appear in list output are retained as-is. These retained files are stored under retained/, keyed by output ID, and are cleaned up once no catalog entry references that output.

Configuration

By default, gocachez reads its config from:

os.UserConfigDir()/gocachez/config.json

and stores cache data in:

os.UserCacheDir()/gocachez

See os.UserConfigDir and os.UserCacheDir for the platform-specific base directories.

Example config:

{
    "cacheDir": "/path/to/gocachez",
    "maxSize": "20GiB",
    "maxAge": "5d",
    "verbose": false
}

Config can also be selected explicitly:

$ go env -w GOCACHEPROG="gocachez -config /path/to/config.json"

Supported options:

Cache directory

  • Config: cacheDir
  • Environment: GOCACHEZ_DIR
  • Flag: -dir
  • Default: os.UserCacheDir()/gocachez

Maximum compressed cache size

  • Config: maxSize
  • Environment: GOCACHEZ_MAX_SIZE
  • Flag: -max-size
  • Default: 20GiB; 0 disables size-based pruning.

Maximum age of unused entries

  • Config: maxAge
  • Environment: GOCACHEZ_MAX_AGE
  • Flag: -max-age
  • Default: 5d, matching cmd/go's GOCACHE; 0 disables age-based pruning. Accepts a duration such as 5d, 36h, or 90m.

Age-based pruning is independent of maxSize: entries and retained files that have not been used within maxAge are trimmed even while the cache is under its size limit.

Verbose maintenance logs

  • Config: verbose
  • Environment: GOCACHEZ_VERBOSE
  • Flag: -v
  • Default: false

Config file path

  • Environment: GOCACHEZ_CONFIG
  • Flag: -config
  • Default: os.UserConfigDir()/gocachez/config.json
  • Missing file is an error when explicitly set.

About

A GOCACHEPROG with zstd compression and auto cleanup

Resources

Stars

Watchers

Forks

Used by

Contributors

Languages