Skip to content

Prune superseded asset-cache extractions after an upgrade - #207

Merged
jasperf merged 2 commits into
mainfrom
chore/prune-extracted-asset-caches
Aug 30, 2026
Merged

Prune superseded asset-cache extractions after an upgrade#207
jasperf merged 2 commits into
mainfrom
chore/prune-extracted-asset-caches

Conversation

@jasperf

@jasperf jasperf commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

A binary installed via Homebrew has no checkout to run from, so it extracts its
embedded scripts to a version-stamped cache directory
(~/Library/Caches/wp-ops/assets-<version>) on first run. Nothing ever removed
the directory the previous version left behind, so the cache grew by one full
tree per upgrade indefinitely — 29 of them, back to 3.23.2, on a machine that
had been upgrading since the Go CLI shipped.

Changes

extractedAssetsRoot() now sweeps the cache parent immediately after a
successful extraction, keeping:

  • the current version's extraction
  • the two most recent superseded extractions (keptPreviousExtractions)
  • anything less than 24 hours old (staleExtractionAge)

Leftover .extract-* temp directories are swept on the same age rule — the
extraction path's own defer removes them, so any survivor belongs to a run
that was killed mid-extraction and owns nothing.

Both guards exist for the same reason sweepStalePlaybookStaging in
go/internal/exec/ansible.go has an age threshold: an older binary may still
be running and reading its own scripts for the length of a database or files
pull, and the version it is reading is most likely the one that was just
replaced. The sweep is best-effort throughout — a cache entry that can't be
read or removed is left alone rather than failing the command the user actually
ran.

Hanging the sweep off the extraction path rather than off every run keeps the
fast path (this version is already extracted) free of any directory scanning.

Testing

go test ./... passes. New coverage in go/cmd/env_test.go:

  • the current version, the most recent superseded extractions, young
    extractions, young temp dirs, and unrelated cache entries all survive
  • older superseded extractions and stale .extract-* dirs are removed
  • a machine with fewer superseded extractions than the keep count loses nothing
  • an unreadable cache parent is a no-op

Each new version extracts its embedded scripts to
~/Library/Caches/wp-ops/assets-<version> and nothing ever removed the
directory the previous version left behind, so the cache grew by one
tree per upgrade indefinitely — 29 of them, back to 3.23.2, on a machine
that had been upgrading since the Go CLI shipped.

Sweep the cache parent right after an extraction succeeds, keeping the
current version, the two most recent superseded extractions, and
anything under 24 hours old. Both guards exist for the same reason the
staged-playbook sweep in internal/exec has an age threshold: an older
binary may still be running, reading its own scripts for the length of a
database or files pull, and the version it is reading is most likely the
one just replaced.

The sweep hangs off the extraction path rather than every run, so the
fast path — this version is already extracted — still does no directory
scanning, and a machine that never upgrades never sweeps because nothing
there is stale.

Stale .extract-* temp directories are swept on the same age rule; the
extraction defer already removes them, so survivors belong to a run that
was killed mid-extraction.
@jasperf
jasperf merged commit 2bd5d8f into main Aug 30, 2026
1 check passed
@jasperf
jasperf deleted the chore/prune-extracted-asset-caches branch August 30, 2026 00:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant