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

fix(current-env): freezing when calling current env #122

Closed
wants to merge 1 commit into from

Conversation

H4ad
Copy link

@H4ad H4ad commented Nov 13, 2024

Related #121
Related #120

This PR basically avoid calling getReport that is well-known for being slow on linux machines with multiple cores (nodejs/node#48831), and also avoid a bug in the excludeNetwork fixed on nodejs/node#55602.

From my tests, the issue with CPU didn't impact the performance, but the excludeNetwork, fixed in the 55602, was the principal issue that was slowing down the npm install.

Instead of copying the code from detect-libc, I prefer to just import the library which has been very well tested.

@H4ad H4ad requested a review from a team as a code owner November 13, 2024 02:41
}
return family
function libc () {
return familySync() || undefined
Copy link
Contributor

@Tofandel Tofandel Nov 20, 2024

Choose a reason for hiding this comment

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

Could you cache locally the result of familySync?

As it can still be a not so fast call (blocking file read operation or blocking get report) and it's done for each dependency in the tree, so it should be cached because it will never change

Copy link
Contributor

Choose a reason for hiding this comment

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

NVM, it seems each subFunction is cached in detect libc, like getReport or getFamily from fs

function isMusl (file) {
return file.includes('libc.musl-') || file.includes('ld-musl-')
}
const { familySync } = require('detect-libc')
Copy link
Member

Choose a reason for hiding this comment

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

This spawns out to the shell to do its detection. I don't know if we want to add that to our security footprint at this time.

Copy link
Contributor

Choose a reason for hiding this comment

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

Only in very rare cases, if ever (because it's only on linux as a fallback to reading the ldd file and the getReport)
https://github.com/lovell/detect-libc/blame/2642d9612fadf14f3eeb5cd56eb3a63b254c7693/lib/detect-libc.js#L144-L154

But if it's a problem we can just take the read from ldd file logic which will solve 99% of problems

Copy link
Member

Choose a reason for hiding this comment

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

I think the ldd file logic is likely the way to go.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll put it in my PR then, could you explain how do you bypass the cache with a separate file and a mock or give an example?

Copy link
Member

Choose a reason for hiding this comment

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

This PR already has some of it, albeit by using requiremock. We use tap so we'd just want to use tap.mock.

I'd say current-env is already sufficiently isolated to be ready for mocking.

Perhaps mocking isn't even the right option here. The require cache is the problem. cleaning it as a t.afterEach may be sufficient.

Copy link
Member

Choose a reason for hiding this comment

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

delete require.cache[require.resolve(module)]

@wraithgar
Copy link
Member

I'm a little bit confused as to why this is happening at all. We built in a guard so that getReport is only being called in linux. It was my understanding that this slowdown only affected Windows.

v7.1.0 fixed this https://github.com/npm/npm-install-checks/blob/main/CHANGELOG.md#710-2024-09-11

... and was added to npm 10.9.0 https://github.com/npm/cli/blob/latest/CHANGELOG.md#1090-2024-10-03

@Tofandel
Copy link
Contributor

Tofandel commented Nov 20, 2024

It was my understanding that this slowdown only affected Windows.

This is where the problem lies, it doesn't happen just in windows, it's only more pronounced there because the DNS implementation seems to be VERY slow (and with WSL it's even worse because DNS queries are not cached)

Get report is in itself a debug function and shouldn't really be used for this. Under the hood it does a lot of expensive synchronus and blocking operations, like reporting on each core of the CPU, reverse DNS queries for each open handle (and with node each request is kept alive by default for 5s keeping the handles open)

And so depending on the system and what happened before in the process, it could take 2ms like it could take 3 minutes to complete, (and that's just for 1 call)

@wraithgar
Copy link
Member

Great, thanks for the education @Tofandel

@@ -1,4 +1,7 @@
const t = require('tap')
const rewiremock = require('rewiremock/node')
Copy link
Member

Choose a reason for hiding this comment

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

Tap has this built in as t.mock

@Tofandel
Copy link
Contributor

Tofandel commented Nov 20, 2024

As for the fix in #118 I'll point you to nodejs/node#55576 (comment) where basically I found that the flag excludeNetwork that was added didn't solve the problem at all, the original PR only went to remove the report on the network adapters which is something that didn't really have any performance issue (and which was pointed out in the original thread and somehow the original PR completely skipped over)

So now when nodejs/node#55602 is released, #118 will have an effect, but right now it doesn't

@wraithgar
Copy link
Member

Ok thanks for everyone's patience waiting for us to get to this, we have a lot of things in flight right now for npm 11, and also an impromptu npm 10 deps update.

I think at the end of the day we want to do two things here. First, we should cache the ultimate values that current-env.js returns. This can just be a variable scoped to the file itself, nothing more. We can delete it from the require cache in tests to get coverage. Second we should introduce ldd parsing/detection in linux, with the getReport as a fallback.

WDYT?

@H4ad
Copy link
Author

H4ad commented Nov 20, 2024

@wraithgar That's ok to me, I will rewrite this PR to not include detect-libc and copy the two detection mechanism (file and getReport), this should solve the current freezing issue and then the team can decide to introduce detect-libc in future versions.

I will try to use the t.mock to be able to get coverage and remove the rewritemock, or delete from the require cache.

@wraithgar
Copy link
Member

I think we have two PRs that are trying to solve the same issue now #120

@H4ad
Copy link
Author

H4ad commented Nov 20, 2024

I will close this in favor of #120 since is basically what I was planning to do.

@H4ad H4ad closed this Nov 20, 2024
@wraithgar
Copy link
Member

Sorry for the confusion there. Thanks for all your help.

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