cmd/go: build cache management
build cache primer
cache structure
The go build cache stores action and data entries used by various go commands. The cache is structured as:
$GOCACHE/
├── 00/
│ ├── <64-hex-ActionID>-a
│ ├── <64-hex-OutputID>-d
│ └── <64-hex-OutputID>-d/
│ └── <binary_name>
├── ...
├── fuzz/
│ └── <import/path>/
│ └── ...
├── trim.txt
├── testexpire.txtIn particular:
trim.txtrecords the last time the build cache was trimmedtestexpire.txtrecords a cutoff date for useable test cache artifacts (go clean -testcachedoesn't actually delete test cache entries)
adding to the build cache
cache keys are a hex sha256 hash of cache inputs. This does mean that we can't know what is cached merely from looking at the cache entry. Data entries should take up the most space, and each should have a corresponding Action entry, but the action entry currently only records the hash mapping of action to output without additional details.
ai summary of artifacts and cache keys3. What Goes into the Cache Key for Each Type of Cached Artifact?
Cache keys (ActionID) are 32-byte salted SHA-256 hashes generated by NewHash. Every hash starts with hashSalt (gover.LocalToolchain()), guaranteeing that different Go toolchain versions or development commits never collide on action keys. Derived subkeys use Subkey(parent, desc) = SHA256("subkey:" + parent + desc).
A. Package Compilation Action Key (Builder.buildActionID)
- Salt:
hashSalt - Tag:
compile\n - Package origin: package directory (if outside GOROOT and not trimmed), or
module <path>@<version>(if-trimpath) - Language version:
go <version>fromgo.mod - Platform:
goos <GOOS> goarch <GOARCH> - Package metadata:
import <ImportPath>,omitdebug,standard,local,prefix,trimpath,forcelibrary - C toolchain configuration: C compiler ID, compiler flags, and toolchain hashes if package uses cgo (
addCToolchainIDs) - Coverage:
cover <mode> <toolID("cover")>(if coverage instrumentation enabled) - Fuzzing: fuzz instrumentation flags (if fuzzing enabled)
- Module build info:
modinfo <string> - Toolchain-specific configuration:
- For
gc: compiler tool ID (b.toolID("compile")), forced gcflags, package gcflags; asm tool ID, forced asmflags, package asmflags (if.sfiles present); architecture environment variables (GOAMD64,GOARM,GOMIPS, etc.); cleanGOEXPERIMENT; compiler debug env vars (GOCLOBBERDEADHASH,GOSSAFUNC,GOSSADIR,GOCOMPILEDEBUG) - For
gccgo: gccgo tool ID, flags, package path, ar tool ID, assembler tool ID
- For
- Input file contents: file path and SHA-256 content hash (
FileHash) of everyGoFiles,CgoFiles,CFiles,CXXFiles,FFiles,MFiles,HFiles,SFiles,SysoFiles,SwigFiles,SwigCXXFiles, andEmbedFiles. - Dependency export data: for each direct imported dependency,
import <ImportPath> <buildExportID(dep.buildID)>(using the content ID of the dependency's export data, not its action ID, enabling convergence in circular bootstrapping). - Preprocessed PGO profile:
pgofile <fileHash(pgoProfile)>(if PGO enabled).
B. Link Action Key (Builder.linkActionID)
- Salt:
hashSalt - Tag:
link\n - Platform and mode: resolved
buildmode,goos <GOOS>,goarch <GOARCH> - Package metadata:
import <ImportPath>,omitdebug,standard,local,prefix,defaultgodebug,trimpath - Linker configuration: linker tool ID (
b.toolID("link")), forced ldflags, package ldflags, arch env variables, cleanGOEXPERIMENT, GOROOT (or empty if-trimpath),GO_EXTLINK_ENABLED - Dependency packages: for every dependency action
a1,packagefile <ImportPath>=<buildObjectID(a1.buildID)>(content ID of package object data). - Main package ID:
packagemain <buildActionID>/<buildObjectID>(ensures the binary's embedded build ID matches). - Shared libraries:
packageshlib <ImportPath>=<buildObjectID(shlib)>(if linking against shared libraries).
C. Separate Export Action Key (Builder.exportActionID)
- Salt:
hashSalt - Export tool ID:
b.toolID("export") importPath,compiler,goVersion- Content hashes of all Go source files
- Export content IDs of dependent packages (
packageFile <ImportPath>=<buildExportID(dep.buildID)>).
D. Vet Analysis Key (Builder.vet)
- Salt:
hashSalt - Vet tool ID:
b.toolID("vet") - Active vet checker flags (
vetFlags) - Root package compile action ID:
pkg <a.Deps[0].actionID> - Content hashes of
.vetxfact export files of all dependencies (vetout <ImportPath> <fileHash(vetx)>). - Subkeys:
- Fix archive:
cache.Subkey(id, "fix.zip") - Output stdout:
cache.Subkey(id, "stdout")
- Fix archive:
E. Test Result Keys (Two-Level Architecture)
- Level 1 Key (
testID) (runTestActor.Act):hashSalt- Content ID of test executable (or linker action ID proxy)
id - Test command line flags (
cacheArgs) - Exec command runner (
work.ExecCmd) - Value stored: test execution log (
# test log).
- Level 2 Key (
testInputsID) (computeTestInputsID): Computed by parsing the recorded# test log:env GODEBUG <hash>getenv <var>: hash of environment variable existence and valuechdir <path>: stat hash of directorystat <path>: file stat info (size, mode, mtime, isDir)open <path>: file stat info (plus directory entry stats if directory; rejects caching if modified< 2sago viaerrFileTooNew)
- Lookup Keys:
- Test output stdout/stderr:
testAndInputKey(testID, testInputsID)=cache.Subkey(testID, "inputs:" + hex(testInputsID)). - Coverage profile:
coverProfileAndInputKey(testID, testInputsID, covMeta)=cache.Subkey(testID, "coverProfile:" + hex(testInputsID) + ...).
- Test output stdout/stderr:
F. Cgo Run & Compile Keys
cgoRunActionID(exec.go): cgo tool ID,GOOS,GOARCH, import path, package directory, C/C++/Fortran compiler flags, msan/asan flags, export header flag, and hashes of Cgo/SWIG files.cgoCompileActionID(exec.go):GOOS,GOARCH, source file name and content hash, compiler flags, package origin, trimpath flag, C toolchain IDs.- Subkeys: Intermediate object files stored under
cache.Subkey(actionID, filename).
G. Preprocessed PGO Profile Key (Builder.pgoActionID)
- Salt:
hashSalt - Preprofile tool ID:
b.toolID("preprofile") - Content hash of raw input pprof file (
b.fileHash(input)).
H. C Compiler Flags & Compiler ID
gccCompilerID(exec.go): Hash of compiler binary path, stat info of compiler binaries, and probed internal compiler ID.gccSupportsFlag:cache.Subkey(compilerID, "gccSupportsFlag " + flag).
I. Module Index Keys (modindex)
- Module hash:
runtime.Version(), index format version,modroot. - Package hash:
runtime.Version(), index format version,modroot,pkgdir, directory entries and stat info (rejects caching if mtime is within 2 seconds).
removing from the build cache
On every command exit, trim.txt is checked.
Cache trimming runs at most once per day.
Entries with mtime older than 5 days are removed.
Accessing a cache entry updates mtime (not atime).
go clean -cache drops the entire cache:
- concurrent
cleanandbuildis unsafe #31948 - cache can be trimmed while still in use #69566
problems people have
cache doesn't cache enough
- cache test binaries #77349
- cache should persist for longer (to have a warm cache in containerized / reproducible builds) #69565
cache caches too much
- caches large embedded resources #59683
- generics bloats build cache, in a low level dep #76946
- make.bash cache entries #61484
- runs out of disk space
- compress the build cache #76337
- clean the build cache based on size (absolute limit, disk relative limit) #29561
- cache trimming takes a long tim #38939
what to do?
A size based cached could solve both wanting to cache more, and caching too much. But it would be adversely affected by storing a lot of recent large binaries (e.g. lots of test runs).
A generational cache like https://go.dev/cl/433295 could let us prune churning entries more effectively.
Source: golang/go