Prewarm Bootsnap caches after brew update#23108
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds an opt-in (by circumstance) Bootsnap cache “prewarm” step after an explicit brew update, intended to shift Ruby/YAML cache compilation work into the time while the user is reading the update report, improving perceived startup latency for subsequent common commands.
Changes:
- Add
Homebrew::Bootsnap.prewarm!to spawn a detached background Ruby process that requiresglobaland common command entrypoints to compile Bootsnap caches. - Invoke
Homebrew::Bootsnap.prewarm!fromupdate-reportonly when the update changedLibrary/Homebrew/vendor, and only for non-auto-update runs. - Add RSpec coverage for
prewarm!behavior and gating.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| Library/Homebrew/startup/bootsnap.rb | Adds prewarm! helper to compile Bootsnap caches in a detached background Ruby process. |
| Library/Homebrew/cmd/update-report.rb | Triggers prewarm after explicit updates that modified Library/Homebrew/vendor. |
| Library/Homebrew/test/startup/bootsnap_spec.rb | Adds specs for Bootsnap.prewarm! spawning behavior and disabled/test gating. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Bootsnap compiles Ruby files into instruction sequence and YAML caches on first load, so an update that changes files leaves the next command to recompile them at startup, on the user's time. - The cost is largest for updates under `Library/Homebrew/vendor`: portable Ruby bumps rotate the whole cache key (it hashes the Ruby version and gem directory listing), cold-starting every file at once, and vendored gem bumps rewrite entire gem trees, much of which `update-report`'s own run never loads and so cannot recompile as a side effect. - Loading the `brew install` and `brew fetch` command graph with a fully cold cache took 0.91s vs 0.56s warm on an M-series Mac in this checkout, so ~350ms of pure cache compilation; the gap is a multiple of that on older machines. - After an explicit `brew update` that changed `Library/Homebrew/vendor`, `update-report` now spawns a detached background Ruby that requires `global`, `cmd/install`, `cmd/fetch` and `cmd/upgrade`, recompiling the caches for the most common next commands while the user reads the update report. - Code-only updates skip the prewarm: `update-report` has already recompiled most of what the next command loads (the residual was ~20-40ms here), so a background process is not worth it. - Auto-update runs also skip it: the command that triggered them loads the same files immediately afterwards, so a parallel prewarm would only duplicate its work. - `Homebrew::Bootsnap.prewarm!` reuses the existing `enabled?` gate, is skipped under `HOMEBREW_TESTS` and spawns the interpreter from `HOMEBREW_RUBY_EXEC_ARGS` with the parent's `$LOAD_PATH`, stdio on `/dev/null` and its own process group, then detaches, so the update exits immediately and a prewarm failure cannot break it. - Verified: across a vendored gem bump a detached child appears once `update-report` exits and compiles whatever its run did not load (736 -> 1055 cache files after a minimal report run), while code-only updates, auto-update runs and `HOMEBREW_NO_BOOTSNAP=1` spawn no child.
183b728 to
faa5bf6
Compare
p-linnane
approved these changes
Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Library/Homebrew/vendor: portable Ruby bumps rotate the whole cache key (it hashes the Ruby version and gem directory listing), cold-starting every file at once, and vendored gem bumps rewrite entire gem trees, much of whichupdate-report's own run never loads and so cannot recompile as a side effect.brew installandbrew fetchcommand graph with a fully cold cache took 0.91s vs 0.56s warm on an M-series Mac in this checkout, so ~350ms of pure cache compilation; the gap is a multiple of that on older machines.brew updatethat changedLibrary/Homebrew/vendor,update-reportnow spawns a detached background Ruby that requiresglobal,cmd/install,cmd/fetchandcmd/upgrade, recompiling the caches for the most common next commands while the user reads the update report.update-reporthas already recompiled most of what the next command loads (the residual was ~20-40ms here), so a background process is not worth it.Homebrew::Bootsnap.prewarm!reuses the existingenabled?gate, is skipped underHOMEBREW_TESTSand spawns the interpreter fromHOMEBREW_RUBY_EXEC_ARGSwith the parent's$LOAD_PATH, stdio on/dev/nulland its own process group, then detaches, so the update exits immediately and a prewarm failure cannot break it.update-reportexits and compiles whatever its run did not load (736 -> 1055 cache files after a minimal report run), while code-only updates, auto-update runs andHOMEBREW_NO_BOOTSNAP=1spawn no child.brewcommands to reproduce the bug?brew lgtm(style, typechecking and tests) locally?Anthropic Fable 5 max with local review and testing.