-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Retry only generating cask variations for supported macOS configurations #20096
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems fine to me TBH.
Caching .from_symbol
makes perfect sense.
I'm a little bit more suspicious about stringify-ing keys in @comparison_cache
, since these can be T.untyped
and I don't know if the string representation captures enough of what we care about. But it's probably fine.
@@ -76,7 +81,7 @@ def <=>(other) | |||
super | |||
end | |||
|
|||
@comparison_cache[other] = result unless frozen? | |||
@comparison_cache[other.to_s] = result unless frozen? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we're comparing frozen objects too often, which prevents caching here. Maybe we should make the comparison_cache belong to the class, instead of the instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeh, feels similar to me: this frozen?
check seems nice to be able to avoid if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't tell you where off the top of my head but we definitely have other classes where we use Cachable
on the class instead of the instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, actually, the from_symbol
cache that I added is class-wide instead of on the instance, and I totally could move this to live there too (which is probably better anyway).
Why does having the cache belong to the class instead of the instance make a difference for frozen objects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you've reproduced the bug and this fixes things: fine with me to merge as-is and then iterate.
Thanks for this!
@@ -76,7 +81,7 @@ def <=>(other) | |||
super | |||
end | |||
|
|||
@comparison_cache[other] = result unless frozen? | |||
@comparison_cache[other.to_s] = result unless frozen? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeh, feels similar to me: this frozen?
check seems nice to be able to avoid if possible.
I've definitely been seeing some messages like this:
I wonder if that could be related and improve performance here too? A nice way of handling that is making a file |
Yep, that's a valid concern. I checked this with versions like "10.15", "11", :big_sur, etc and it seems fine, but I didn't check this with all of the other types of
Yeah, I've seen this a bunch too. I'll take a look |
I have a local branch where (among other things) I took care of all of the type changes to upgrade |
@@ -62,7 +67,7 @@ def initialize(version) | |||
|
|||
sig { override.params(other: T.untyped).returns(T.nilable(Integer)) } | |||
def <=>(other) | |||
return @comparison_cache[other] if @comparison_cache.key?(other) | |||
return @comparison_cache[other.to_s] if @comparison_cache.key?(other.to_s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This definitely isn't correct as it assumes everything is compared on String level which is not true:
MacOS.version == :sequoia
=> true
MacOS.version == "sequoia"
=> false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A compromise could be to special case this to version objects since we can assume they are compared equivalently to strings.
cache_key = other.is_a?(Version) ? other.to_s : other
This should keep the optimisation you have here while not regressing the above example.
Out of interest, I checked to see where the comparison cache is needed. A lot of the time spent is runtime type checking, which we could disable for version.rb
comparison methods (while keeping static type checking) given the runtime type safety there is not particularly useful anyway due to the T.untyped
nature of <=>
. Comparison cache will always be quicker given it's simply less instructions, but you can make the uncached case over 5x faster by reducing type checks.
@samford that would be great |
The PR to upgrade |
Retry #20080
To be honest, I’m still not entirely sure what went wrong, so I would encourage others to investigate this more. I tried using
brew prof
, but wasn’t able to get particularly useful info (although this is probably me not using it properly).Here is what I found. For some reason, it seems like the
generate-cask-api
job hangs when processing theipartition
cask. As far as I can tell, there doesn’t seem to be anything particularly unique about that cask. It only works on macOS versions up to High Sierra, and while it does have a variation block, thedepends_on
line is outside.If I manually run
:ipartition.c.to_hash_with_variations
, it seems to work just fine without hanging. Similarly, if I modifybrew generate-cask-api
to skip all casks exceptipartition
, it does not hang. However, when I modifybrew generate-cask-api
to skip all casks that don’t start with the letteri
, it does hang. If I modifybrew generate-cask-api
to include all casks exceptipartition
, it doesn’t seem to hang.From this, it seems like there is something special about the
ipartition
cask that causes the generation to hang, but only when processed with others. There is definitely more investigating to be done.I ended up determining that the line that hands is inside
MacOSRequirement#allows?
, where twoMacOSVersion
objects are compared using<=
:brew/Library/Homebrew/requirements/macos_requirement.rb
Line 83 in d428e83
This comparison is reached many times per-cask, and for some reason, when processing
ipartition
, this line gets slower and slower each time. By the time we are comparing11
to10.13
(the latter being the maximum version allowed by thedepends_on
, we’re already pretty slow. By the time we get to comparing10.14
to10.13
, I wasn’t able to run the entire comparison before I gave up and exited (which took at least 10 minutes).If we look at the
MacOSVersion#<=>
method, it caches each comparison to avoid needing to compute the comparisons over and over again:brew/Library/Homebrew/macos_version.rb
Lines 64 to 82 in d428e83
Since the cache key is a
MacOSVersion
object, I thought maybe the issue was that we were recursively comparingMacOSVersions
which was causing the issue. I also thought maybe we were creating newMacOSVersion
objects each time, and these weren’t comparing properly.So, I made two changes to
MacOSVersion
that are included in this PR:::from_symbol
so the same object is returned each time a symbol is requested#<=>
, use the stringified version of the other object being compared as the cache key to make theincludes?
check a simple string comparisonBoth of these solutions seemed to work on their own to solve this, and I was able to run
brew generate-cask-api
in ~30 seconds on my machine (compared to longer than 10 minutes before, I never waited long enough to know if it actually returned).Clearly, this is weird and I think I’ve gone a little too far down the rabbit hole to really know what the best solution is, so I would appreciate extra eyes from anyone who's willing to do some investigating
I don't really want to merge this as-is because my gut tells me there is either a better way to handle this all together or something else is the root of the problem