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

adds freezeCoreModules configuration option to mitigate memory leaks #8331

Conversation

lev-kazakov
Copy link

resolves #6399, resolves #6814

Summary

Some commonly used libraries like agent-base and graceful-fs override node core modules' (a.k.a built-in modules) methods.
This causes a memory leak in jest since the long lived node core modules' state is now bound to the short lived test execution context.
Each time a new test runs, jest creates a new context, the context is then bound to the long lived core module state, thus increasing the leak.

Test plan

Tests were added here:
https://github.com/lev-kazakov/jest/blob/25fb8ba198878bef6f8b25baac0b79ce57f33fda/packages/jest-runtime/src/__tests__/runtime_require_module.test.js#L186-L211
For an e2e test, you can run the following test suite with this branch linked:
https://github.com/lev-kazakov/jest-leak-fixer/tree/master/test


Prevents overriding node's core module methods so to prevent memory leaks.

Attempt to override such a property will fail silently.
Copy link
Author

Choose a reason for hiding this comment

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

our > 10K test suite runs successfully with these changes, so i guess that in most cases, there is no actual need/use of these overrides.


### `freezeCoreModulesWhitelist` [Array<string>]

Default: `['crypto']`
Copy link
Author

Choose a reason for hiding this comment

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

Proxies have issues with bound native methods, hence crypto.randomBytes will fail. Tried to hack it from multiple directions with no success.

@@ -374,6 +378,8 @@ export type ProjectConfig = {
extraGlobals: Array<keyof NodeJS.Global>;
filter: Path | null | undefined;
forceCoverageMatch: Array<Glob>;
freezeCoreModules: boolean;
Copy link
Author

Choose a reason for hiding this comment

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

not quite sure if these should be on ProjectConfig or GlobalConfig. probably GlobalConfig 😵 . kindly assist.

@lev-kazakov lev-kazakov force-pushed the add-freeze-core-modules-configuration-option branch 2 times, most recently from d89a5bb to 7bee921 Compare April 14, 2019 19:02
@SimenB
Copy link
Member

SimenB commented Apr 18, 2019

@scotthovestadt thoughts on this one?

@lev-kazakov
Copy link
Author

@SimenB , @scotthovestadt
another approach would be to allow users run tests without isolation.
i.e use native node require instead of jest's.
this would probably require creating a new environment + other refactoring.

@SimenB
Copy link
Member

SimenB commented Apr 18, 2019

That's not really an option - if you want that Jest is not for you :) Almost all of the features in jest depends on the isolation (mocks, transformations, fake timers etc).

@codecov-io
Copy link

codecov-io commented Apr 24, 2019

Codecov Report

Merging #8331 into master will increase coverage by 0.22%.
The diff coverage is 56.93%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #8331      +/-   ##
==========================================
+ Coverage   64.73%   64.96%   +0.22%     
==========================================
  Files         277      283       +6     
  Lines       11709    12104     +395     
  Branches     2876     2990     +114     
==========================================
+ Hits         7580     7863     +283     
- Misses       3512     3604      +92     
- Partials      617      637      +20
Impacted Files Coverage Δ
e2e/v8-coverage/no-sourcemap/x.css 100% <ø> (ø)
e2e/coverage-remapping/covered.ts 100% <ø> (ø) ⬆️
packages/jest-reporters/src/utils.ts 80.64% <ø> (ø) ⬆️
packages/jest-core/src/getNoTestFound.ts 42.85% <ø> (ø) ⬆️
packages/jest-core/src/getNoTestFoundVerbose.ts 15% <ø> (ø) ⬆️
packages/jest-reporters/src/get_snapshot_status.ts 100% <ø> (ø) ⬆️
packages/jest-diff/src/constants.ts 100% <ø> (ø) ⬆️
packages/jest-config/src/Deprecated.ts 66.66% <ø> (ø) ⬆️
...ackages/jest-reporters/src/get_snapshot_summary.ts 86.84% <ø> (ø) ⬆️
...ckages/jest-core/src/lib/active_filters_message.ts 100% <ø> (ø) ⬆️
... and 99 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 220835c...bde98f6. Read the comment docs.

@sibelius
Copy link

I've made a patch-package of this pull request if more people wants to test this tests against 24.7.1

https://gist.github.com/sibelius/f98e62dd9346ddc97f07fe3814dc1b6e

https://github.com/ds300/patch-package

@sibelius
Copy link

More me the main aggressor is agent-base

Error: Trying to override method 'request' of a frozen core module 'https'
    at Object.<anonymous> (/app/node_modules/@slack/client/node_modules/agent-base/patch-core.js:45:15)

@sibelius
Copy link

using this pull requests keeps my tests below 200MB

@lev-kazakov
Copy link
Author

@SimenB , @scotthovestadt
ping

@shusson
Copy link

shusson commented May 28, 2019

any updates on this PR?

It's a pain to debug memory leaks due to hotpatching in some deep dependency and it only affects us when testing.

@vroudge
Copy link

vroudge commented Jun 14, 2019

Is this going to be merged? We'd really benefit a lot from this PR over here.

@lev-kazakov
Copy link
Author

@SimenB , @scotthovestadt any thoughts on this one?

value: any,
receiver: any,
) => {
if (typeof value !== 'function') {
Copy link

Choose a reason for hiding this comment

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

Here I had to also avoid caching in a case when value._isMockFunction was true, otherwise jest.spyOn would refuse to work (i.e. jest.spyOn(fs, "readFileSync"))

Copy link
Author

Choose a reason for hiding this comment

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

done

@sibelius
Copy link

sibelius commented Nov 1, 2019

@lev-kazakov can you rebase?

@lev-kazakov lev-kazakov force-pushed the add-freeze-core-modules-configuration-option branch from 105fc9b to bde98f6 Compare November 3, 2019 09:04
@lev-kazakov
Copy link
Author

@sibelius done.

@lev-kazakov
Copy link
Author

@SimenB , @scotthovestadt is there a chance we get this thing reviewed/merged/denied/whatever? :)

@shusson
Copy link

shusson commented Dec 18, 2019

Ping @SimenB , @scotthovestadt any time to look at this PR?

We have reached the point at which we are trying to avoid upgrading packages in our main app because of the potential leaks that might happen during testing 😬

@yang6lang1
Copy link

Hi @lev-kazakov @dir01 ,

I am from a company that is heavily using Jest for unit testing. In our test, we have a requirement to use 'setupFiles' config to load some large other 3rd party dependencies into the global variable. Hence, our test suites are suffering great memory leak (mostly due to grateful-fs).
I looked into this PR and attempt to apply it directly in our 'jest-runtime' dependency, seems it is fixing the problem.
Just curious if we have any plans to put this change in?
I am really grateful if you can let me know the plan, so I could include it in my company's Jira bug report.

Thanks,
Alex Yang

@yang6lang1
Copy link

Hi @lev-kazakov,

Our team was able to temporarily fix our memory leak issues by using your fix in the CI, we just have one question:

  • If we cache all the core modules here, is this going to affect the test independence? Reusing existing libraries is fine as long as they are not stateful. If they are stateful then they can easily lead to breaks and unpredictable behaviour

We did find some problems with this fix. In our test, we are using Nock: https://github.com/nock/nock
This library modifies some functions in 'http' module, and looks like it cannot be cached as in your fix, so we had to add it to the whitelist for not caching.

We are looking forward to hearing some thoughts from you, and are very interested in whether this change is planning to be merged.

Regards,
Alex Yang

@lev-kazakov
Copy link
Author

Hi @yang6lang1,
I'm not from Facebook :)
I'm a bit tired of trying to push this thing, if you may, kindly find some Facebook devs in the wild and mention them here :)
Cheers.

@green-arrow
Copy link

green-arrow commented Apr 29, 2020

Thanks for the reply! Unfortunately, I haven’t been able to track down which modules are leaking. There are no console warning or errors when using the patch. Using —detectOpenHandles doesn’t surface anything, and —detectLeaks says that my tests are leaking but no other information.

Edit: fwiw, I have agent-base pinned to 5.1.1 and graceful-fs pinned to 4.2.4, both of which I believed are versions with the memory leaks fixed.

@sibelius
Copy link

try to profile both cpu and memory like this

node --cpu-prof --heap-prof ./node_modules/.bin/jest /path/to/test/withLeak

@davydkov
Copy link

davydkov commented May 4, 2020

I think this option can dramatically improve experience in finding modules, that are leaking.

FauxFaux added a commit to snyk/resolve-package that referenced this pull request Jul 18, 2020
then-fs seems to be causing a memory leak in jest in other tools.

Found by jestjs/jest#8331
@FauxFaux
Copy link
Contributor

This has helped me find then-fs, and docker-modem (which has a copy-paste of follow-redirects, which also isn't fixed), and various new, exciting ways to reach agent-base, including axios and gaxios and nodemailer and google cloud's tooling.

I feel that this useful contribution allows me also to add a "plus one", which follows.

I spent probably days trying to find this with a heap debugger. These memory leaks are a killer for us; with ts-jest you start out at a leak of about 450MB, and we're seeing >50MB per test. We have approaching a thousand tests we'd like to run.

We work around this by splitting the test run manually, completely negating most of the benefits of jest.

A "quick fix" for this problem for us is to use the above Jest patch to find places where we're importing something, and change it to a lazy require(), so at least most tests don't fail.

import { foo } from 'dodgy-module';

function a() { /* not foo */ }
function b() { foo(); }

becomes

function a() { /* not foo */ }
function b() { require('dodgy-module')(); }

...totally ruining our TS and linting rules and, in some places, the code structure.

@sibelius
Copy link

I think this could be useful to find leaks

FauxFaux added a commit to snyk/resolve-package that referenced this pull request Jul 18, 2020
then-fs seems to be causing a memory leak in jest in other tools.

Found by jestjs/jest#8331
FauxFaux added a commit to snyk/policy that referenced this pull request Jul 18, 2020
then-fs monkey patches the stdlib, which causes problems for jest, c.f.
jestjs/jest#8331

promise-fs is a drop-in replacement here, for us.
FauxFaux added a commit to snyk/resolve-package that referenced this pull request Jul 18, 2020
then-fs seems to be causing a memory leak in jest in other tools.

Found by jestjs/jest#8331
@sibelius
Copy link

jest@26.1.0 does not have more memory leaks for our codebase

this patch was breaking ws package tests, so we removed it for now

@sibelius
Copy link

latest version with node 14 still leaking

I think it is graceful-fs again

@sibelius
Copy link

can we rebase this once again?

@lev-kazakov
Copy link
Author

can we rebase this once again?

hi, sorry, no capacity.
if jest people agree to merge this, i'll rebase once again.

@sinbargit
Copy link

Jest 25.5 has been released with the graceful-fs fix

@lev-kazakov Just be clear, the jest25.5 or ts-jest 25.5 or jest-core25.5. I will fix my verison

@lev-kazakov
Copy link
Author

Jest 25.5 has been released with the graceful-fs fix

@lev-kazakov Just be clear, the jest25.5 or ts-jest 25.5 or jest-core25.5. I will fix my verison

i think you meant to tag @SimenB. i'm not part of the jest team.

@sinbargit
Copy link

sinbargit commented Apr 28, 2021

Jest 25.5 has been released with the graceful-fs fix

@lev-kazakov Just be clear, the jest25.5 or ts-jest 25.5 or jest-core25.5. I will fix my verison

i think you meant to tag @SimenB. i'm not part of the jest team.

I like using your branch as a temp solution, till that merges into master. But still not very understand why

override node core modules' (a.k.a built-in modules) methods

cause memory leak

@github-actions
Copy link

github-actions bot commented Sep 8, 2022

This PR is stale because it has been open 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.

@github-actions github-actions bot added the Stale label Sep 8, 2022
@derN3rd
Copy link

derN3rd commented Sep 9, 2022

Dear github bot, please don't close this useful PR, until we finally have a better solution for the huge memory leaks in jest or ts-jest

Sincerely,
a dev

@github-actions github-actions bot removed the Stale label Sep 9, 2022
@github-actions
Copy link

github-actions bot commented Sep 9, 2023

This PR is stale because it has been open 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.

@github-actions github-actions bot added the Stale label Sep 9, 2023
@github-actions
Copy link

github-actions bot commented Oct 9, 2023

This PR was closed because it has been stalled for 30 days with no activity. Please open a new PR if the issue is still relevant, linking to this one.

@github-actions github-actions bot closed this Oct 9, 2023
Copy link

github-actions bot commented Nov 9, 2023

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 9, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Jest leaks memory from required modules with closures over imports Jest memory problems in node environment