Skip to content

upgrade: parallelize bottle tab fetching#22118

Open
ZhongRuoyu wants to merge 2 commits intomainfrom
parallel-tab-fetch
Open

upgrade: parallelize bottle tab fetching#22118
ZhongRuoyu wants to merge 2 commits intomainfrom
parallel-tab-fetch

Conversation

@ZhongRuoyu
Copy link
Copy Markdown
Member

Currently, FormulaInstaller#fetch_bottle_tab is called sequentially and silently in the background for each formula to upgrade. When there is a large number of formulae to upgrade, this can lead to a significant delay before the any output is shown to the user.

Instead, let's parallelize the bottle tab fetching. This makes the upgrade progress much faster, and also visibly shows the user that something is happening.

A naive and rough benchmark involving 8 outdated formulae shows that this shortens Upgrade#formula_installers from ~4.7s to ~2.1s. With more outdated formulae, this difference should become even more significant.


  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them? Performance claims (e.g. "this is faster") must include Hyperfine benchmarks.
    • NB. I didn't benchmark this with Hyperfine due to the complexity of only timing the tab fetch logic. I did include a very basic comparison though.
  • Have you written new tests (excluding integration tests) for your changes? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) with your changes locally?

  • AI was used to generate or assist with generating this PR. Please specify below how you used AI to help you, and what steps you have taken to manually verify the changes. Non-maintainers may only have one AI-assisted/generated PR open at a time.

Copilot AI review requested due to automatic review settings May 1, 2026 15:53
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to speed up brew upgrade startup and provide visible progress by fetching bottle tab manifests concurrently instead of sequentially per formula, reducing perceived “hang time” when many formulae are upgraded.

Changes:

  • Introduces a shared Homebrew::DownloadQueue to enqueue bottle tab manifest downloads for all upgrade targets.
  • Fetches the queued bottle tab downloads in parallel, then proceeds with installer filtering/selection.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +109 to +115
installers.filter_map do |fi|
all_runtime_deps_installed = fi.bottle_tab_runtime_dependencies.presence&.all? do |dependency, hash|
minimum_version = if (version = hash["version"])
Version.new(version)
end
Dependency.new(dependency).installed?(minimum_version:, minimum_revision: hash["revision"].to_i)
end
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bottle_tab_runtime_dependencies is never populated in this method (it’s only set in FormulaInstaller#prelude), so all_runtime_deps_installed will always be nil here and this dependent-skipping logic will never trigger. Consider deriving the runtime deps directly from fi.formula.bottle_tab_attributes["runtime_dependencies"] after the queue fetch, or otherwise populating bottle_tab_runtime_dependencies before this check (without running the full prelude).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is a valid point and a pre-existing issue.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6498306.

@ZhongRuoyu ZhongRuoyu marked this pull request as draft May 1, 2026 16:35
Copy link
Copy Markdown
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice idea and work so far! This makes sense broadly but I think a few changes would help:

  • should be done for install/reinstall/upgrade
  • should be in an existing download queue if possible so the parallelisation can occur with other downloads (this may not be possible! if so: please explain why both here and in a Ruby comment)

@ZhongRuoyu
Copy link
Copy Markdown
Member Author

ZhongRuoyu commented May 2, 2026

should be done for install/reinstall/upgrade

This is already being done for install 1 and reinstall 2 via Install.fetch_formulae or Install.enqueue_formulae, both of which then calls FormulaInstaller.prelude_fetch and fetches the bottle tab while in the download queue. Here in upgrade we specifically need bottle tabs fetched ahead of the normal Install.fetch_formulae pipeline due to the reason below.

should be in an existing download queue if possible so the parallelisation can occur with other downloads (this may not be possible! if so: please explain why both here and in a Ruby comment)

I don't think that works cleanly, because here in Upgrade.formula_installers, we need bottle tab data to determine which formulae to include:

all_runtime_deps_installed = fi.bottle_tab_runtime_dependencies.presence&.all? do |dependency, hash|
minimum_version = if (version = hash["version"])
Version.new(version)
end
Dependency.new(dependency).installed?(minimum_version:, minimum_revision: hash["revision"].to_i)
end
if !dry_run && dependents && all_runtime_deps_installed
# Don't need to install this bottle if all of the runtime
# dependencies have the same or newer version already installed.
next
end

And this logic is specific to upgrade (although it isn't really working as expected due to the issue Copilot pointed out above 😭).

Footnotes

  1. https://github.com/Homebrew/brew/blob/4a95077682ae5f342cf60ff181ebbcfb03d49117/Library/Homebrew/cmd/install.rb#L342

  2. https://github.com/Homebrew/brew/blob/4a95077682ae5f342cf60ff181ebbcfb03d49117/Library/Homebrew/cmd/reinstall.rb#L196-L232

Currently, `FormulaInstaller#fetch_bottle_tab` is called sequentially
and silently in the background for each formula to upgrade. When there
is a large number of formulae to upgrade, this can lead to a significant
delay before the any output is shown to the user.

Instead, let's parallelize the bottle tab fetching. This makes the
upgrade progress much faster, and also visibly shows the user that
something is happening.

A naive and rough benchmark involving 8 outdated formulae shows that
this shortens `Upgrade#formula_installers` from ~4.7s to ~2.1s. With
more outdated formulae, this difference should become even more
significant.
@ZhongRuoyu ZhongRuoyu force-pushed the parallel-tab-fetch branch from bce0d1a to 6498306 Compare May 2, 2026 14:38
@ZhongRuoyu ZhongRuoyu marked this pull request as ready for review May 2, 2026 14:38
@ZhongRuoyu ZhongRuoyu requested a review from Copilot May 2, 2026 14:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Library/Homebrew/upgrade.rb
Comment thread Library/Homebrew/formula_installer.rb Outdated
@ZhongRuoyu ZhongRuoyu marked this pull request as draft May 2, 2026 14:55
Fix the current situation where `fi.bottle_tab_runtime_dependencies` is
never populated, which effectively makes the runtime dependency check
for skipping unnecessary upgrades a no-op.
@ZhongRuoyu ZhongRuoyu force-pushed the parallel-tab-fetch branch from 6498306 to ca85c3f Compare May 2, 2026 15:42
@ZhongRuoyu ZhongRuoyu marked this pull request as ready for review May 2, 2026 16:07
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.

3 participants