Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tap: fix performance regression in *_files_by_name #16791

Merged
merged 2 commits into from
Mar 2, 2024

Conversation

apainintheneck
Copy link
Contributor

@apainintheneck apainintheneck commented Mar 2, 2024

  • 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?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew style with your changes locally?
  • Have you successfully run brew typecheck with your changes locally?
  • Have you successfully run brew tests with your changes locally?

We essentially stopped caching these accidentally and they get called every time we try to load a cask or formula from the API. It gets really, really, really slow.

I ran brew deps --casks --eval-all before and after the changes.

Before

I let it run for 3 minutes before killing it. No output had been printed to the screen.

After

It finished printing all output (pages and pages of it) in less than a minute.


This should match the caching behavior we had before the recent changes in these two PRs.

We essentially stopped caching these accidentally and they get
called every time we try to load a cask or formula from the API.
It gets really, really, really slow.

I ran `brew deps --casks --eval-all` before and after the changes.

I let it run for 3 minutes before killing it. No output had been
printed to the screen.

It finished printing all output (pages and pages of it) in less
than a minute.

---

This should match the caching behavior we had before the
recent changes in these two PRs.

- #16777
- #16775
@apainintheneck apainintheneck added the bug Reproducible Homebrew/brew bug label Mar 2, 2024
@apainintheneck
Copy link
Contributor Author

apainintheneck commented Mar 2, 2024

I found another one. Since it's related to other ones, I just added it to this PR.


Tap.reverse_tap_migrations_renames should be a relatively small hash but it gets recalculated every time we call Cask#oldnames or Formula#oldnames which ends up being a lot if you run certain commands with --eval-all.

For example, when running the following, it took 10% of the total runtime.

brew deps --casks --eval-all

This should be a relatively small hash but it gets recalculated
every time we call Cask#oldnames or Formula#oldnames which ends
up being a lot if you run certain commands with --eval-all.

For example, when running the following, it took 10% of the total
runtime.

```
brew deps --casks --eval-all
```
@apainintheneck apainintheneck marked this pull request as draft March 2, 2024 04:15
@apainintheneck apainintheneck force-pushed the fix-perfomance-regression-in-tap-files_by_name branch from 5493649 to 2da1c93 Compare March 2, 2024 04:21
@apainintheneck apainintheneck marked this pull request as ready for review March 2, 2024 04:21
Copy link
Member

@Bo98 Bo98 left a comment

Choose a reason for hiding this comment

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

Thanks for catching this quickly!

@@ -1279,7 +1281,7 @@ def cask_tokens
def cask_files_by_name
return super if Homebrew::EnvConfig.no_install_from_api?

Homebrew::API::Cask.all_casks.each_with_object({}) do |item, hash|
@cask_files_by_name ||= Homebrew::API::Cask.all_casks.each_with_object({}) do |item, hash|
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, we probably should avoid Pathname here too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed.

@Bo98 Bo98 merged commit 4e65af0 into master Mar 2, 2024
26 checks passed
@Bo98 Bo98 deleted the fix-perfomance-regression-in-tap-files_by_name branch March 2, 2024 05:00
@@ -820,7 +820,7 @@ def formula_reverse_renames

sig { returns(T::Hash[String, T::Array[String]]) }
def self.reverse_tap_migrations_renames
Tap.each_with_object({}) do |tap, hash|
cache[:reverse_tap_migrations_renames] ||= Tap.each_with_object({}) do |tap, hash|
Copy link
Member

Choose a reason for hiding this comment

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

@apainintheneck, I remember specifically not caching this because it will never be cleared, except in tests.

Basically, every instance's #clear_cache would need to clear the global cache[:reverse_tap_migrations_renames], because it depends on all taps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My understanding is that Tap.reverse_tap_migrations_renames loads each tap with Tap.each and then calls Tap#tap_migrations on each tap to build this list. Do we expect either Tap.each or Tap#tap_migrations to change over the life of the program?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess you're worried about the case where a tap gets installed with Tap#ensure_installed! after Tap.reverse_tap_migrations_renames has been built, right? Maybe it'd just be a good idea to clear the class level cache when we do that since Tap#ensure_installed! should only very rarely need to install a new tap.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or we could just go back to how things are. Either way works for me.

Copy link
Member

@Bo98 Bo98 Mar 2, 2024

Choose a reason for hiding this comment

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

Could just have the cache inside the Tap.each_with_object instead and cache by cache[:reverse_tap_migrations_renames][tap.name], or have an instance method so it's tied to instance cache.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now that I look at it a bit more the problem might be Tap.each in this case.

Screen Shot 2024-03-02 at 10 51 57 AM

This is from running brew prof -- deps --casks --eval-all.

Copy link
Member

Choose a reason for hiding this comment

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

A special cache of installed tap instances that gets reset on Tap#install and Tap#uninstall would make sense to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That sounds doable. I'm not sure how that could be implemented in a simple, straightforward way though. This seemed like a no-brainer but now I know it wasn't.

Copy link
Member

Choose a reason for hiding this comment

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

A special cache of installed tap instances that gets reset on Tap#install and Tap#uninstall would make sense to me.

@Bo98 @apainintheneck @reitermarkus I think we should/could just nuke all tap caches on those methods given how infrequently they are called.

@github-actions github-actions bot added the outdated PR was locked due to age label Apr 3, 2024
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 3, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Reproducible Homebrew/brew bug outdated PR was locked due to age
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants