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

Resource leak when (re-)using an abort signal #2160

Closed
marcelmeulemans opened this issue Oct 10, 2022 · 2 comments · Fixed by #2162
Closed

Resource leak when (re-)using an abort signal #2160

marcelmeulemans opened this issue Oct 10, 2022 · 2 comments · Fixed by #2162
Labels
bug Something does not work as it should

Comments

@marcelmeulemans
Copy link
Contributor

Describe the bug

  • Node.js version: v16.17.1
  • OS & version: Debian 5.10.140-1

When re-using an abort controller for multiple got requests a significant amount of memory is retained by the AbortSignal through got. The main reason for this (I think) is that the Request instance adds an event listener for the abortevent on the passed signal where the event handler refers back to the request. The event handler is never removed so the signal retains a reference to the Request instance for as long as it lives.

Actual behavior

The code to reproduce below downloads and discards a 1MB file in a loop. The loop exists when the abort signal is aborted via ctrl-c. The same abort signal is passed to got to cancel the in-progress download. The heap profile screenshot below shows how a large chunk of memory is retained via the callback context of an event listener coming from here:
https://github.com/sindresorhus/got/blob/main/source/core/index.ts#L254

screenshot_20221010_221207

Expected behavior

Memory is released after the request completes.

Code to reproduce

import got from 'got';

const abortController = new AbortController();

process.on('SIGINT', function () {
  abortController.abort();
});

async function main() {
  while (!abortController.signal.aborted) {
    await got.get(
      'https://ia902805.us.archive.org/14/items/1mbFile/1mb.mp4',
      {
        signal: abortController.signal,
      }
    ).buffer();
  }
}

main().then(() => console.log('Bye')).catch((e) => {
  if (e.constructor.name !== 'AbortError') {
    console.error(e);
  }
});

Checklist

  • [*] I have read the documentation.
  • [*] I have tried my code with the latest version of Node.js and Got.
marcelmeulemans added a commit to marcelmeulemans/got that referenced this issue Oct 10, 2022
Not sure that this fix is entirely correct because I may have missed clean
up points. Also, needs a test still.

Fixes sindresorhus#2160
@sindresorhus
Copy link
Owner

// @jopemachine

@szmarczak szmarczak added the bug Something does not work as it should label Oct 10, 2022
@szmarczak
Copy link
Collaborator

got/source/core/index.ts

Lines 254 to 256 in 623229f

this.options.signal?.addEventListener('abort', () => {
this.destroy(new AbortError(this));
});

this should be

		this.options.signal?.addEventListener('abort', () => {
			this.destroy(new AbortError(this));
		}, { once: true });

additionally, we should do

this.options.signal?.removeEventListener('abort', abortListener);

somewhere in _destroy.

jopemachine added a commit to jopemachine/got that referenced this issue Oct 10, 2022
marcelmeulemans added a commit to marcelmeulemans/got that referenced this issue Oct 11, 2022
Not sure that this fix is entirely correct because I may have missed clean
up points. Also, needs a test still.

Fixes sindresorhus#2160
marcelmeulemans added a commit to marcelmeulemans/got that referenced this issue Oct 12, 2022
Not sure that this fix is entirely correct because I may have missed clean
up points. Also, needs a test still.

Fixes sindresorhus#2160
marcelmeulemans added a commit to marcelmeulemans/got that referenced this issue Nov 16, 2022
Not sure that this fix is entirely correct because I may have missed clean
up points. Also, needs a test still.

Fixes sindresorhus#2160
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something does not work as it should
Projects
None yet
3 participants