Skip to content

fix(pruner): evict whole bun packages instead of single files - #27

Merged
azlekov merged 2 commits into
mainfrom
fix/bun-cache-whole-package-eviction
Oct 19, 2026
Merged

azlekov merged 2 commits into
mainfrom
fix/bun-cache-whole-package-eviction

Conversation

@azlekov

@azlekov azlekov commented Oct 19, 2026 •

Copy link
Copy Markdown

Problem

prune_caches ran lru_cap "$runner/bun", which LRU-evicts single files by atime across the whole /root/.bun volume. bun treats install/cache/<pkg>@<ver>@@@1/ as complete whenever the folder exists, so an evicted file silently goes missing from every later node_modules.

0-byte files go first, because copying them never reads them and their atime never moves. On the hetzner-cx43-runner-* runners (cap 8 GiB, pruner logged evicting least recently used files from /caches/3/bun on 10-16 and 10-17), next@16.3.8 lost dist/compiled/server-only/empty.js. After that, every Next build failed with Module not found: Can't resolve 'server-only' (dodi-smart/nis-suite#142). The same eviction can also delete /root/.bun/bin/bun.

Fix

  • New lru_cap_bun, modelled on lru_cap_mise_installs. It evicts whole package folders, oldest package.json atime first (dir mtime when there is no package.json), until the whole volume fits under the cap (excess_bytes on the volume). This covers both cache/<pkg>@<ver>@@@1 and scoped cache/@scope/<pkg>@<ver>@@@1. It also removes the matching cache/<pkg>/<ver>@@@1 symlink and any empty name/scope dirs.
  • It never touches bin/ or the @t@ manifest piles. It clears *.tmp / *.part leftovers and cache/.tmp/* entries older than 60 min; the age guard keeps it away from installs that are still running.
  • prune_caches now calls lru_cap_bun "$runner/bun".
  • hk stays on lru_cap: its volume holds configs/<hash>.json and pklr/packages/<hash>.{zip,url}. Each of these is an independent whole file that gets regenerated or re-downloaded when it is missing. There are no per-tool/version trees, and the volume is ~1 MB, far below the cap.
  • pruner.sh returns early when it is sourced, so tests can call its functions.
  • Updated the header comment and the README "Nightly" section.

Test

tests/pruner-bun.sh (plain bash, needs GNU find/stat/du) builds a fake bun volume. It holds packages with 0-byte files that have very old atimes, a scoped package, the version symlinks, @t@ piles, bin/bun/bunx, and stale and live temp entries. It then runs prune_caches with a cap that requires dropping exactly the three oldest packages. A new scripts job in the PR build runs shellcheck and tests/*.sh, and the gate job now requires it.

$ docker run --rm -v "$PWD":/w:ro ubuntu:24.04 bash /w/tests/pruner-bun.sh
[pruner] ... /tmp/.../caches/1/bun is 1002394B (cap 744173B)
[pruner] ... evicting least recently used packages from /tmp/.../caches/1/bun/install/cache
[pruner] ... /tmp/.../caches/1/bun/install/cache freed ~300039B
PASS: bun cache evicted by whole package (volume 702228B)

Against main's pruner.sh, the same test fails 16 checks, among them next lost server-only/empty.js and bin/bun removed. shellcheck -x pruner.sh tests/pruner-bun.sh is clean.

Separately, CACHE_CAP_BYTES on the cx43 host was raised from 8 GiB to 12 GiB (62 GB free on that disk; done outside this PR).

🤖 Generated with Claude Code

azlekov and others added 2 commits October 19, 2026 17:45
lru_cap evicted single files across /root/.bun by atime. bun reuses install/cache/<pkg>@<ver>@@@1/ as complete
whenever it exists, so an evicted file vanished from every later node_modules. 0-byte files went first (copying
never reads them, so their atime never moves): next@16.3.8 lost dist/compiled/server-only/empty.js and every Next
build on the cx43 runners failed with "Can't resolve 'server-only'". It could also evict bin/bun.

lru_cap_bun drops whole package folders (scoped ones included), oldest package.json atime first, along with their
<pkg>/<ver> symlinks, until the volume fits the cap; it never touches bin/ or the @t@ piles and clears temp leftovers
older than an hour. hk stays on lru_cap: its entries are independent whole files (config json, pkl zips).

Adds tests/pruner-bun.sh (fails 16 checks on the old code) and a shellcheck + tests job to the PR build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@azlekov
azlekov merged commit 4f0c22e into main Oct 19, 2026
13 checks passed
@azlekov
azlekov deleted the fix/bun-cache-whole-package-eviction branch October 19, 2026 15:16
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