-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
Conversation
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. |
There was a problem hiding this 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?
@nodejs/http |
@leidegre Thanks for all the work you did to track this down! |
@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. |
@leidegre it should be sufficient to verify that Anyway it can be added also in follow up PR. |
@leidegre feel free to add the following test under '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);
}));
}));
})); |
d5cf198
to
85b3bd7
Compare
@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. |
85b3bd7
to
5057d36
Compare
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? |
@addaleax Mind reader! 👍 |
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.
I just saw your comment, that’s all 😄 |
Looks like some of the checks have failed with some transient Git issue? I don't know if I can do anything about it... |
Everything good now? |
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>
Landed in 26f1500 |
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>
Backport currently blocked on #32329. |
@codebytere This is important to me. Is there anything I can do to unblock this? |
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>
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
andparser[kOnTimeout]
tonull
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.