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

formula_installer: use cached fetched formula instance when available #15778

Merged
merged 1 commit into from Aug 15, 2023

Conversation

Bo98
Copy link
Member

@Bo98 Bo98 commented Jul 28, 2023

Had just about enough energy to get this one done tonight too.

We detect whether we want an API formula or need a Ruby source formula fairly late on (after the formula installer has been first created), so we swap the formula instance within the installer.

Unfortunately, dependencies use separate instances of the installer for their fetch and install stages so we can't rely on the fetch method being called like we could normally. The dependency handling should probably be refactored to not create separate instances like that as it'll eliminate these type of bugs occurring in the future (or we alternatively commit to separate fetcher and installer classes), as well as help tackle the growingly fragile amount of assumptions FormulaInstaller currently has on the way it is called (e.g. some steps are incorrectly cached without considering some options, but we get away with it based on how things are set up on the CLI side and how things like formula options are rarely used in complex dependency trees). But that's a non-trivial task that I definitely won't have time for this week.

Fixes #15765 (comment)

Comment on lines +1185 to +1191
sig { returns(T.nilable(Formula)) }
def previously_fetched_formula
# We intentionally don't compare classes here.
self.class.fetched.find do |fetched_formula|
fetched_formula.full_name == formula.full_name && fetched_formula.active_spec_sym == formula.active_spec_sym
end
end
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Would it make sense to memoize this if it could get called 3 times but we don't expect the result to change? It should be a cheap check either way so not a big deal.

Copy link
Member

Choose a reason for hiding this comment

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

Yeh, that'd be another option instead of using a e.g. Hash here

Copy link
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.

Looks good so far, thanks @Bo98! If you're confident in this fixing #15765 (comment): could also roll those changes into this PR, too.

@@ -1179,9 +1182,17 @@ def fetch_dependencies
deps.each { |dep, _options| fetch_dependency(dep) }
end

sig { returns(T.nilable(Formula)) }
def previously_fetched_formula
# We intentionally don't compare classes here.
Copy link
Member

Choose a reason for hiding this comment

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

Would be good to explain why we don't.

Copy link
Member

Choose a reason for hiding this comment

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

@Bo98 Merging as-is but would love you to address this (and any other comments you feel like) in a follow-up when you get the chance! Thanks for creating it ❤️

Copy link
Member

Choose a reason for hiding this comment

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

(I would do it myself but I'm not sure I know the answer 😅)

Copy link
Member Author

Choose a reason for hiding this comment

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

Quick answer is: while each formula definition is its own subclass, a API and from-source formula class are separate classes but we want to equate them to be the same thing here given mixing bottle and from-source installs of the same formula within the same operation doesn't make sense.

Copy link
Member

Choose a reason for hiding this comment

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

@Bo98 Thanks, opened a PR: #15869

sig { returns(T.nilable(Formula)) }
def previously_fetched_formula
# We intentionally don't compare classes here.
self.class.fetched.find do |fetched_formula|
Copy link
Member

Choose a reason for hiding this comment

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

Might be nicer to use a Hash instead of Set to make this sort of lookup a bit nicer.

Comment on lines +1185 to +1191
sig { returns(T.nilable(Formula)) }
def previously_fetched_formula
# We intentionally don't compare classes here.
self.class.fetched.find do |fetched_formula|
fetched_formula.full_name == formula.full_name && fetched_formula.active_spec_sym == formula.active_spec_sym
end
end
Copy link
Member

Choose a reason for hiding this comment

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

Yeh, that'd be another option instead of using a e.g. Hash here

@gerlero
Copy link
Contributor

gerlero commented Jul 30, 2023

Looks good so far, thanks @Bo98! If you're confident in this fixing #15765 (comment): could also roll those changes into this PR, too.

I've tried this PR with a cherry-pick of #15765; can confirm it fixes the problem from my comment.

@MikeMcQuaid MikeMcQuaid merged commit d7268ae into Homebrew:master Aug 15, 2023
24 checks passed
@Bo98 Bo98 deleted the dep-source-fix branch August 15, 2023 19:00
@github-actions github-actions bot added the outdated PR was locked due to age label Sep 15, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
outdated PR was locked due to age
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants