-
-
Notifications
You must be signed in to change notification settings - Fork 32.8k
http: improve http parser performance #59621
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
base: main
Are you sure you want to change the base?
Conversation
Review requested:
|
Co-Authored-by: Jonas Badalic <[email protected]>
4d3aad7
to
05606e5
Compare
@@ -49,8 +49,6 @@ namespace node { | |||
V(HTTP2STREAM) \ | |||
V(HTTP2PING) \ | |||
V(HTTP2SETTINGS) \ | |||
V(HTTPINCOMINGMESSAGE) \ | |||
V(HTTPCLIENTREQUEST) \ |
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.
Hmm.. it occurs to me now that we've never really considered if removing these constitutes a breakig (semver-major) change. This does, afterall, change the number that is associated with the other providers observable via require('async_hooks').asyncWrapProviders
. I know that async hooks as a whole are still considered experimental but it's been around for so long that changes here could actually be breaking for some people. @nodejs/tsc .. thoughts?
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.
With AsyncLocalStorage migrated away from async_hooks at this point, I don't think there's any significant remaining use of async_hooks, so I would not feel too worried about it. 🤔
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.
In the past we didn't care about this. I know about at least one case where some internal refactoring caused a rename of an entry in this list.
env()->context(), object(), 0, nullptr); | ||
|
||
if (r.IsEmpty()) callback_scope.MarkAsFailed(); | ||
USE(cb.As<Function>()->Call(env()->context(), object(), 0, nullptr)); |
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.
Is this actually correct for correct error propagation? @addaleax
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.
Doesn't seem like it, no.
I honestly don't quite get what the idea behind this PR is. Maybe @anonrig or @mcollina can expand on this a bit more, and ideally incorporate it into the commit message, so that it's accessible not just for us as reviewers but also for future context. But as it stands, the correct title for the PR should be http: remove async tracking
, not http: improve http parser performance
.
} else { | ||
// Since the Parser destructor isn't going to run the destroy() callbacks | ||
// it needs to be triggered manually. | ||
parser.free(); |
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.
can you investigate if you can avoid the setImmediate before?
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.
I didn't dive deeper but removing it breaks the following 3 tests
out/Release/node --test-reporter=./test/common/test-error-reporter.js --test-reporter-destination=stdout /home/yagiz/coding/node/test/sequential/test-http-regr-gh-2928.js
out/Release/node --test-reporter=./test/common/test-error-reporter.js --test-reporter-destination=stdout /home/yagiz/coding/node/test/parallel/test-http-upgrade-server2.js
out/Release/node --test-reporter=./test/common/test-error-reporter.js --test-reporter-destination=stdout /home/yagiz/coding/node/test/parallel/test-set-http-max-http-headers.js
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.
I think it really depends on the breakage and errors for those.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #59621 +/- ##
==========================================
- Coverage 89.84% 89.81% -0.03%
==========================================
Files 667 667
Lines 196260 196217 -43
Branches 38563 38553 -10
==========================================
- Hits 176332 176238 -94
- Misses 12382 12420 +38
- Partials 7546 7559 +13
🚀 New features to boost your workflow:
|
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.
I'm not sure why we had async_hooks in the first place.
We have the AsyncWrap integration, and more generally 'callback handling' logic (CallbackScope
or MakeCallback()
) for the same reason we have it anywhere else in Node.js -- when we are entering JS from C++ (without a JS call stack underneath), we need to take care of things like handling uncaught exceptions and running the microtask queue, and, well, async context tracking.
Minimal regression test for the type of bug that would be introduced by this PR:
'use strict';
const { get, createServer } = require('node:http');
const { executionAsyncId } = require('node:async_hooks');
const assert = require('node:assert');
createServer((req, res) => {
assert.notStrictEqual(executionAsyncId(), 0); // this test makes this fail
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello, World!\n');
}).listen(0, function() {
get(`http://localhost:${this.address().port}/`, () => this.close());
});
|
||
if (r.IsEmpty()) | ||
got_exception_ = true; | ||
if (try_catch.HasCaught()) got_exception_ = true; |
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.
What happens to these exceptions?
env()->context(), object(), 0, nullptr); | ||
|
||
if (r.IsEmpty()) callback_scope.MarkAsFailed(); | ||
USE(cb.As<Function>()->Call(env()->context(), object(), 0, nullptr)); |
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.
Doesn't seem like it, no.
I honestly don't quite get what the idea behind this PR is. Maybe @anonrig or @mcollina can expand on this a bit more, and ideally incorporate it into the commit message, so that it's accessible not just for us as reviewers but also for future context. But as it stands, the correct title for the PR should be http: remove async tracking
, not http: improve http parser performance
.
Also, just going to echo what @Flarna said in the original PR:
I agree that the most meaningful start for making this sort of optimization is to ensure that we have the necessary test coverage before jumping to conclusions about what is and isn't feasible or acceptable. |
see corresponding comment in original PR: While I agree that HTTP parser itself is not async HTTP requests are. As a result I'm fine to the change of http parser itself but the async hooks functionality itself should preserved on HTTP request level otherwise then. Quite a while ago I tried such an approach to use the |
Continues the work of @JonasBa on #57938
Improves the performance of http parser by 5% by removing the async_hooks on an synchronous http parser. I'm not sure why we had async_hooks in the first place. @mcollina was the first person who made me realize about this, so kudos to him for realizing this in unneeded.