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

Memory leak when using fetch and not consuming the response body #51162

Closed
mcat95 opened this issue Dec 15, 2023 · 3 comments
Closed

Memory leak when using fetch and not consuming the response body #51162

mcat95 opened this issue Dec 15, 2023 · 3 comments
Labels
fetch Issues and PRs related to the Fetch API

Comments

@mcat95
Copy link

mcat95 commented Dec 15, 2023

Version

v21.4.0

Platform

Linux 6.6.7-arch1-1 #1 SMP PREEMPT_DYNAMIC Thu, 14 Dec 2023 03:45:42 +0000 x86_64 GNU/Linux

Subsystem

fetch API

What steps will reproduce the bug?

Execute the following sample script with node --expose-gc:

const repro = async () => {
  for (let i = 0; i < 1000; i++) {
    await fetch('https://raw.githubusercontent.com/nodejs/node/main/doc/changelogs/CHANGELOG_V20.md');
    console.log(process.memoryUsage().rss);
  }
}

repro()
  .then(() => console.log('Finished'))
  .catch(err => console.log('Error', err));

How often does it reproduce? Is there a required condition?

It always happen

What is the expected behavior? Why is that the expected behavior?

The logged memory usage of the process should be consistent and not increase over every execution of the fetch

What do you see instead?

The memory usage of the process increases with every request, and it's not ever garbage collected (Even with calling global.gc() on every execution.

Additional information

If you add a .then(r => r.text()) or something else that uses the body, the memory leak goes away. I found a similar issue in the node-fetch library (node-fetch/node-fetch#83), but I wasn't able to find this leak in nodejs itself.

For a workaround, I found that using a Finalization Register, I can create a wrapper function that detects evictions of unneeded response objects, and, if the body was unused, empty the body like this:

const registry = new FinalizationRegistry(response => {
  if (!response || response.bodyUsed) return;
  response.arrayBuffer().catch(console.log);
});
@marco-ippolito marco-ippolito added the fetch Issues and PRs related to the Fetch API label Dec 15, 2023
@marco-ippolito
Copy link
Member

cc @nodejs/undici

@ronag
Copy link
Member

ronag commented Dec 15, 2023

This is expected. https://undici.nodejs.org/#/?id=garbage-collection

@ronag ronag closed this as completed Dec 15, 2023
@cxcorp
Copy link

cxcorp commented Aug 28, 2024

In case anyone else bumps into this, it appears that this behavior has changed and since v20.16.0 the body stream gets closed when the Response is garbage collected. This change was in Undici v6.16.0 via nodejs/undici#3199

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fetch Issues and PRs related to the Fetch API
Projects
None yet
Development

No branches or pull requests

4 participants