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 aborting fetch requests #2198

Closed
jamesdiacono opened this issue Jul 28, 2023 · 12 comments
Closed

Memory leak when aborting fetch requests #2198

jamesdiacono opened this issue Jul 28, 2023 · 12 comments
Labels
bug Something isn't working

Comments

@jamesdiacono
Copy link

Bug Description

A memory leak occurs when frequently aborting fetch requests.

Reproducible By

Run the following code in Node.js.

import undici from "undici";

(function attack() {
    const controller = new AbortController();
    undici.fetch(
        "http://idonotexist:9999",
        {signal: controller.signal}
    ).catch(attack);
    controller.abort();
}());

In less than a minute, I see memory usage of the Node.js process climb to several gigabytes.

Expected Behavior

Memory usage to remain stable, as is the case when you comment out the controller.abort(); statement.

Environment

$ uname -a
Darwin MacBook-Pro.local 22.5.0 Darwin Kernel Version 22.5.0: Thu Jun  8 22:22:20 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T6000 arm64 arm Darwin
$ node -v
v20.4.0
@jamesdiacono jamesdiacono added the bug Something isn't working label Jul 28, 2023
@jamesdiacono jamesdiacono changed the title Memory leak when aborting fetch requestors Memory leak when aborting fetch requests Jul 28, 2023
@mcollina
Copy link
Member

looks like we are not clearing up resources in case of an abort

@metcoder95
Copy link
Member

Does the same happens for request or others, or only within fetch?

I’ll try to look at it later Today

@KhafraDev
Copy link
Member

cc @ronag

@jamesdiacono
Copy link
Author

@metcoder95 I'm sorry, I don't know. I have never used request.

@metcoder95
Copy link
Member

No worries! But I was referring to undici.request 😅

@Uzlopak
Copy link
Contributor

Uzlopak commented Jan 29, 2024

Yes, as @KhafraDev mentioned it is basically
nodejs/node#43655

It is because of pendingUnhandledRejections keeping references on the promises, so that node can remove it it later from the WeakMap.

https://github.com/nodejs/node/blob/64c6d97463c29bade4d6081683dab2cd7cda298d/lib/internal/process/promises.js#L161

@Uzlopak
Copy link
Contributor

Uzlopak commented Jan 29, 2024

@KhafraDev

Can or should we even implement a solution in undici?

@KhafraDev
Copy link
Member

Can - yes (?), as your PR did that.

Should - yes, we can always revert the change once it's fixed upstream. We've implemented plenty of workarounds for bugs in node core in the past.

@Uzlopak
Copy link
Contributor

Uzlopak commented Feb 13, 2024

This is a bug, which needs to be fixed in node core, as it is a "bug" in the event loop. This bug is basically introduced by the logic which handles the unhandledPromiseRejected warning. Basically this line results in this issue:

https://github.com/nodejs/node/blob/544cfc5ef151bca8d625fbccc581200a77b00bc0/lib/internal/process/promises.js#L161

So because we keep a reference to the promise for "at least one tick" v8 can not garbage collect the promise. So what happens is that before the event loop can go to the "next" iteration, it gets another promise to handle and starts again processing the same iteration of the event loop as processPromiseRejections returns true.

https://github.com/nodejs/node/blob/544cfc5ef151bca8d625fbccc581200a77b00bc0/lib/internal/process/task_queues.js#L96

@mcollina
Copy link
Member

The previous promises should be collectable, no? Why is it staying alive? If that's the case you should be able to reproduce it just with an abortcontroller or a self-rejecting promise chain.

@Uzlopak
Copy link
Contributor

Uzlopak commented Feb 13, 2024

Exactly. You dont need fetch. You can use the example in the corresponding node issue nodejs/node#43655

@mcollina
Copy link
Member

Then, we close this.

@mcollina mcollina closed this as not planned Won't fix, can't repro, duplicate, stale Feb 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants