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

http: fixes memory retention issue with FreeList and HTTPParser #33190

Closed

Conversation

leidegre
Copy link
Contributor

@leidegre leidegre commented May 1, 2020

Fixes: #29394
Refs: #33167 (comment)

The issue here is that because the HTTPParser is pooled it has the possibility to act as a retainer of a lot of data. In summary we have to reset parser.onIncoming and parser[kOnTimeout] to null to prevent these from retaining memory while the HTTPParser is sitting in the FreeList unused.

I'd like this to eventually be cherry picked into the Node 14 release.

See the reference for details.


@ronag Here's your PR.

@nodejs-github-bot nodejs-github-bot added the http Issues or PRs related to the http subsystem. label May 1, 2020
@leidegre
Copy link
Contributor Author

leidegre commented May 1, 2020

Adding this for clarification.

This isn't a strict memory leak issue. There's an upper limit to the free list of a 1000 entries, which means that at most, it will retain up to 1000 times whatever the amount of bytes retained by event handlers. In our case this was about 1.5 MB (our heap usage eventually grew to about 1.5 GB as the free list is filled up when the site is under heavier load, this takes several days to happen).

This issue has resulted in production outages due to memory starvation and I'm very eager to get this fixed.

@ronag ronag added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label May 1, 2020
@nodejs-github-bot
Copy link
Collaborator

nodejs-github-bot commented May 1, 2020

Copy link
Member

@Trott Trott left a comment

Choose a reason for hiding this comment

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

Not a blocker to landing this, but is it possible to add a test?

@Trott
Copy link
Member

Trott commented May 2, 2020

@nodejs/http

@Trott
Copy link
Member

Trott commented May 2, 2020

@leidegre Thanks for all the work you did to track this down!

@leidegre
Copy link
Contributor Author

leidegre commented May 2, 2020

@Trott Thanks.

I understand the desire to add a test but I'm not sure how I would go about that in a meaningful manner given the particulars of the code changes. It would be a lot of work for me to make a meaningful contribution.

I would be happy to verify any commits/builds though.

@lpinca
Copy link
Member

lpinca commented May 2, 2020

@leidegre it should be sufficient to verify that parser.onIncoming is null after that the parser object is returned to the pool. There could already be a test that does something similar to use as a reference.

Anyway it can be added also in follow up PR.

@lpinca
Copy link
Member

lpinca commented May 3, 2020

@leidegre feel free to add the following test under test/parallel

'use strict';

const common = require('../common');
const assert = require('assert');
const http = require('http');
const { HTTPParser } = require('_http_common');

// Test that the `HTTPParser` instance is cleaned up before being returned to
// the pool to avoid memory retention issues.

const kOnTimeout = HTTPParser.kOnTimeout | 0;
const server = http.createServer();

server.on('request', common.mustCall((request, response) => {
  const parser = request.socket.parser;

  assert.strictEqual(typeof parser[kOnTimeout], 'function');

  request.socket.on('close', common.mustCall(() => {
    assert.strictEqual(parser[kOnTimeout], null);
  }));

  response.end();
  server.close();
}));

server.listen(common.mustCall(() => {
  const request = http.get({ port: server.address().port });
  let parser;

  request.on('socket', common.mustCall(() => {
    parser = request.parser;
    assert.strictEqual(typeof parser.onIncoming, 'function');
  }));

  request.on('response', common.mustCall((response) => {
    response.resume();
    response.on('end', common.mustCall(() => {
      assert.strictEqual(parser.onIncoming, null);
    }));
  }));
}));

@leidegre leidegre force-pushed the bugfix/http-parser-memory-leak branch from d5cf198 to 85b3bd7 Compare May 3, 2020 07:00
@leidegre
Copy link
Contributor Author

leidegre commented May 3, 2020

@lpinca Thanks! I've included the test in the PR (I've amended this commit). I've also verified that the test fails with Node 14 and that it succeeds with these changes applied.

The test is here test/parallel/test-http-parser-memory-retention.js.

@leidegre leidegre force-pushed the bugfix/http-parser-memory-leak branch from 85b3bd7 to 5057d36 Compare May 3, 2020 07:05
@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

@leidegre
Copy link
Contributor Author

leidegre commented May 4, 2020

This is currently an issue with Node 12 (as well as 14) so I was wondering what the process is for landing this in 12 (given that it's the current LTS) and that 14 will become the next LTS? Do I need to do something for this to happen or will this be added to some list and integrated into the next release?

@leidegre
Copy link
Contributor Author

leidegre commented May 4, 2020

@addaleax Mind reader! 👍

@addaleax
Copy link
Member

addaleax commented May 4, 2020

Do I need to do something for this to happen or will this be added to some list and integrated into the next release?

Commits are backported to LTS by default, so unless there are merge conflicts, no. If there are merge conflicts, you will be pinged, though. Also, just so you know, as a rule commits have to be released for 2 weeks in a Current (i.e. 14.x) release before they are backported to LTS release lines.

I’ve added the lts-watch label, but that’s more in order to make sure that this is taken into consideration and to explicitly mark this as something that should land on lts-v12.x.

@addaleax Mind reader!

I just saw your comment, that’s all 😄

@leidegre
Copy link
Contributor Author

leidegre commented May 5, 2020

Looks like some of the checks have failed with some transient Git issue? I don't know if I can do anything about it...

@nodejs-github-bot
Copy link
Collaborator

@nodejs-github-bot
Copy link
Collaborator

nodejs-github-bot commented May 5, 2020

@leidegre
Copy link
Contributor Author

leidegre commented May 5, 2020

Everything good now?

Flarna pushed a commit that referenced this pull request May 5, 2020
Fixes: #29394
Refs: #33167 (comment)

PR-URL: #33190
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
@Flarna
Copy link
Member

Flarna commented May 5, 2020

Landed in 26f1500

@Flarna Flarna closed this May 5, 2020
@leidegre leidegre deleted the bugfix/http-parser-memory-leak branch May 6, 2020 05:35
codebytere pushed a commit that referenced this pull request May 7, 2020
Fixes: #29394
Refs: #33167 (comment)

PR-URL: #33190
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
@codebytere codebytere mentioned this pull request May 18, 2020
@codebytere
Copy link
Member

Backport currently blocked on #32329.

@leidegre
Copy link
Contributor Author

leidegre commented Jun 7, 2020

@codebytere This is important to me. Is there anything I can do to unblock this?

@codebytere
Copy link
Member

@leidegre if you're willing, this could be backported together with #32329

@Flarna
Copy link
Member

Flarna commented Aug 18, 2020

Removed backport-blocked label because #32329 has been backported via #34131

addaleax pushed a commit that referenced this pull request Sep 22, 2020
Fixes: #29394
Refs: #33167 (comment)

PR-URL: #33190
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
@codebytere codebytere mentioned this pull request Sep 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. http Issues or PRs related to the http subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Keepalive prevents garbage collection on unread responses