| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
Review requested:
|
Sorry, something went wrong.
There was a problem hiding this comment.
lgtm
Sorry, something went wrong.
Failed to start CI⚠ Commits were pushed since the last approving review: ⚠ - http: optimize IncomingMessage._dump ✘ Refusing to run CI on potentially unsafe PRhttps://github.com/nodejs/node/actions/runs/17445334596 |
Sorry, something went wrong.
There was a problem hiding this comment.
I understand that this is behind an option but I find it odd. It breaks the invariant where 'end' / 'close' are always emitted.
Sorry, something went wrong.
There was a problem hiding this comment.
This is a valid point. I don't think it's anywhere documented that it must emit the life cycle events. Though it is indeed breaking. It is totally valid from the streams api since you can receive an already closed stream. It will work with stream.finished and stream.pipeline.
Sorry, something went wrong.
|
@mcollina @RafaelGSS benchmark CI is not meaningful, I added my own autocannon benchmark #59747 (comment) const http = require('http');
http.createServer((req, res) => {
res.end();
}).listen(9000);npx autocannon http://localhost:9000 |
Sorry, something went wrong.
|
Can we implement so that if there is someone watching for ‘end’ or ‘close’, it follows the usual path, while if they are not, we can avoid them? Alternatively, we could have _dump actually emit them. |
Sorry, something went wrong.
|
With fastify: nxt-23.0$ npx autocannon http://localhost:9000/ Running 10s test @ http://localhost:9000/ 10 connections ┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬────────┐ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ ├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼────────┤ │ Latency │ 0 ms │ 0 ms │ 0 ms │ 0 ms │ 0.03 ms │ 0.84 ms │ 137 ms │ └─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴────────┘ ┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬───────────┬─────────┐ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼───────────┼─────────┤ │ Req/Sec │ 23,711 │ 23,711 │ 50,911 │ 79,487 │ 54,421.6 │ 22,087.53 │ 23,701 │ ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼───────────┼─────────┤ │ Bytes/Sec │ 4.32 MB │ 4.32 MB │ 9.27 MB │ 14.5 MB │ 9.9 MB │ 4.02 MB │ 4.31 MB │ └───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴───────────┴─────────┘ Req/Bytes counts sampled once per second. # of samples: 10 544k requests in 10.04s, 99 MB read nxt-23.0$ npx autocannon http://localhost:9000/ Running 10s test @ http://localhost:9000/ 10 connections ┌─────────┬──────┬──────┬───────┬──────┬─────────┬─────────┬───────┐ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ ├─────────┼──────┼──────┼───────┼──────┼─────────┼─────────┼───────┤ │ Latency │ 0 ms │ 0 ms │ 0 ms │ 0 ms │ 0.01 ms │ 0.27 ms │ 76 ms │ └─────────┴──────┴──────┴───────┴──────┴─────────┴─────────┴───────┘ ┌───────────┬─────────┬─────────┬────────┬─────────┬─────────┬──────────┬─────────┐ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ ├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼──────────┼─────────┤ │ Req/Sec │ 68,287 │ 68,287 │ 82,175 │ 84,223 │ 81,056 │ 4,446.14 │ 68,272 │ ├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼──────────┼─────────┤ │ Bytes/Sec │ 12.4 MB │ 12.4 MB │ 15 MB │ 15.3 MB │ 14.8 MB │ 809 kB │ 12.4 MB │ └───────────┴─────────┴─────────┴────────┴─────────┴─────────┴──────────┴─────────┘ Req/Bytes counts sampled once per second. # of samples: 10 811k requests in 10.02s, 148 MB read |
Sorry, something went wrong.
|
Benchmark result on my tests: So, tuns out my computer was too fast and it was delivering the same amount of req/sec regardless of binary. In other words, I was being limited by wrk2 itself. Now I have configured proper values and the latency (which is the most important metric in terms of wrk2) has shown the following distribution: 50.000% 731.00us 75.000% 1.02ms 90.000% 1.22ms 99.000% 1.85ms 99.900% 2.05ms 99.990% 2.15ms 99.999% 2.26ms 100.000% 2.49ms node v24.5.0 50.000% 693.00us 75.000% 0.98ms 90.000% 1.16ms 99.000% 1.51ms 99.900% 1.99ms 99.990% 2.18ms 99.999% 2.60ms 100.000% 2.94ms And autocannon reported: node v24.5.0 3164k requests in 30.04s, 576 MB read versus 4007k requests in 30.04s, 729 MB read |
Sorry, something went wrong.
| req._readableState.endEmitted = true; | ||
| req._readableState.destroyed = true; | ||
| req._readableState.closed = true; | ||
| req._readableState.closeEmitted = true; |
There was a problem hiding this comment.
Maybe move this stream internal handling into some function exported by the stream implementation?
Sorry, something went wrong.
There was a problem hiding this comment.
_dumped is specific to http.
Sorry, something went wrong.
There was a problem hiding this comment.
I refer to all the req._readableState.* assignments which are not http specific to my knowledge. The look more like stream internals.
Sorry, something went wrong.
|
It seems the user facing option is gone now and this behavior is no opt in anymore. The repeated forced-pushes make this PR a bit hard to follow. I think the PR headline/description should be improved a bit. Currently a reader might not expect that this optimization of an internal function is actually a breaking change. |
Sorry, something went wrong.
|
Changed to draft. |
Sorry, something went wrong.
Co-authored-by: Gürgün Dayıoğlu <hey@gurgun.day>
Indeed - that's definitely a problem imo, and this behaviour has to be optional (at least an option to disable it, but probably a opt-in option for a smoother transition). There will definitely be plenty of cases in production where users do actually use GET bodies even though they shouldn't (we even have examples in our own tests, which have been fixed here) and there's real-world scenarios (like the ElasticSearch API) where supporting bodies with GET is required. Happy to discourage users from doing this, but we do have to support it for people who need it. |
Sorry, something went wrong.
|
Just for context, even for Fastify we get a lot of people wanting to have a body in GET requests unfortunately |
Sorry, something went wrong.
|
One option is to check the content-length header and only apply this optimization if it's not 0? |
Sorry, something went wrong.
|
For express I ran a quick run through our (work in progress) tooling on 22.19.0-bookworm and @ronag suggested to share here as well. $ npm run load -- --test="@expressjs/perf-load-example" --overrides='{"express":"O4FDev/express#feat/dump-bodies-get-head"}'
$ npm run load -- --test="@expressjs/perf-load-example"
$ npm run compare perf/load/example/results/result-1757081729427.json perf/load/example/results/result-1757081805041.json
> @expressjs/perf-wg@1.0.0 compare
> expf compare perf/load/example/results/result-1757081729427.json perf/load/example/results/result-1757081805041.json
A Results: perf/load/example/results/result-1757081729427.json
==============================================================
┌─────────┬─────────┬─────────┬─────────┬─────────┬────────────┬───────────┬─────────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼─────────┼─────────┼─────────┼─────────┼────────────┼───────────┼─────────┤
│ Latency │ 1082 ms │ 1119 ms │ 2807 ms │ 3710 ms │ 1358.19 ms │ 534.84 ms │ 5211 ms │
└─────────┴─────────┴─────────┴─────────┴─────────┴────────────┴───────────┴─────────┘
┌───────────┬─────┬──────┬─────────┬─────────┬───────────┬──────────┬────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼─────┼──────┼─────────┼─────────┼───────────┼──────────┼────────┤
│ Req/Sec │ 0 │ 0 │ 22,943 │ 23,599 │ 18,744.95 │ 6,995.89 │ 1,081 │
├───────────┼─────┼──────┼─────────┼─────────┼───────────┼──────────┼────────┤
│ Bytes/Sec │ 0 B │ 0 B │ 7.81 MB │ 8.04 MB │ 6.38 MB │ 2.38 MB │ 368 kB │
└───────────┴─────┴──────┴─────────┴─────────┴───────────┴──────────┴────────┘
Req/Bytes counts sampled once per second.
# of samples: 60
1151k requests in 60.02s, 383 MB read
26k errors (0 timeouts)
B Results: perf/load/example/results/result-1757081805041.json
==============================================================
┌─────────┬─────────┬─────────┬─────────┬─────────┬────────────┬───────────┬─────────┐
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
├─────────┼─────────┼─────────┼─────────┼─────────┼────────────┼───────────┼─────────┤
│ Latency │ 1299 ms │ 1338 ms │ 2003 ms │ 2516 ms │ 1393.62 ms │ 196.76 ms │ 2914 ms │
└─────────┴─────────┴─────────┴─────────┴─────────┴────────────┴───────────┴─────────┘
┌───────────┬─────┬──────┬─────────┬─────────┬───────────┬──────────┬────────┐
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
├───────────┼─────┼──────┼─────────┼─────────┼───────────┼──────────┼────────┤
│ Req/Sec │ 0 │ 0 │ 19,359 │ 19,935 │ 18,376.02 │ 4,066.35 │ 2,805 │
├───────────┼─────┼──────┼─────────┼─────────┼───────────┼──────────┼────────┤
│ Bytes/Sec │ 0 B │ 0 B │ 6.59 MB │ 6.79 MB │ 6.26 MB │ 1.38 MB │ 955 kB │
└───────────┴─────┴──────┴─────────┴─────────┴───────────┴──────────┴────────┘
Req/Bytes counts sampled once per second.
# of samples: 60
1129k requests in 60.02s, 375 MB read
26k errors (0 timeouts)
Comparison: equal
=================
{
diff: { rps: '2.01%', throughput: '2.01%', latency: '-2.54%' },
a: {
avgRPS: 18744.95,
avgThroughput: 6382649.6,
avgLatency: 1358.19,
status: { '200': { count: 1124707 } }
},
b: {
avgRPS: 18376.02,
avgThroughput: 6256686.94,
avgLatency: 1393.62,
status: { '200': { count: 1102508 } }
}
}
Notes for folks on these results: The tooling runs while capturing perf events and with the inspector protocol on, it is more designed for debugging performance than benchmarking but it should be pretty reliable for doing comparisons over small patches like this.
Same on Express. Although I would love to make this default and have an option to allow GET with body. |
Sorry, something went wrong.
We'd also need to check for transfer-encoding with chunked as well, but yes sniffing for this doesn't seem unreasonable to me and should be safe... The RFC lists all the possibilities here, and says you can send response bodies with neither header (in which case body is everything until the end of the connection) but not request bodies. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
PRE:
POST: