Conversation
|
This fails with the following: node$ make -j4 && out/Release/node --expose-gc /Users/ronagy/GitHub/nxtedition/node/test/parallel/test-gc-tls-external-memory.js
AssertionError [ERR_ASSERTION]: 0 < 256
at TLSSocket.connect (/Users/ronagy/GitHub/nxtedition/node/test/parallel/test-gc-tls-external-memory.js:36:5)
at TLSSocket.<anonymous> (/Users/ronagy/GitHub/nxtedition/node/test/common/index.js:365:15)
at TLSSocket.emit (events.js:314:20)
at emitErrorNT (internal/streams/destroy.js:127:8)
at emitErrorCloseNT (internal/streams/destroy.js:92:3)
at processTicksAndRejections (internal/process/task_queues.js:80:21)
at runNextTicks (internal/process/task_queues.js:62:3)
at processImmediate (internal/timers.js:435:9) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '=='
}Seems like a GC related bug in |
|
@nodejs/streams @addaleax |
lundibundi
left a comment
There was a problem hiding this comment.
LGTM. We should probably run a benchmark just to make sure it's fine.
|
Just a note, this does not pass CI because of the GC issue. I will need help to sort that out. |
|
This is blocking #34035 |
|
Very weird, after looking through heap-dumps of master and this PR it looks like |
@lundibundi Would you mind creating an issue for that? I'll apply your workaround in the meantime. |
|
That seems to have fixed it. I'd just like a thumbs up from @addaleax before landing this. |
|
My only concern here is the increased memory usage but I guess it's ok as we are in an error, hopefully uncommon, condition. Is it possible to add a test for #34103 (comment)? |
|
So … let’s be a bit more clear about the cause of the memory leak here: What’s happening is that the In the test, this turns out to be somewhat catastrophic, because each error’s stack frame contains the previous TLS socket, effectively creating a singly-linked list of all TLS sockets here. So yes, this PR does introduce this problem for a specific reason. I know this is very dependent on the test’s layout, but it’s a problem I could see arising in other situations as well. There’s four possible paths forward that I see here:
I would personally lean towards option 3, but I think any is fine. If we do pick option 2, it probably makes more sense to amend the test code than the tls source code, though. [diff for option 3]diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js
index b63c8f89aec5..223e9af7bc74 100644
--- a/lib/_stream_writable.js
+++ b/lib/_stream_writable.js
@@ -434,12 +434,14 @@ function onwrite(stream, er) {
if (er) {
if (!state.errored) {
state.errored = er;
+ er.stack;
}
// In case of duplex streams we need to notify the readable side of the
// error.
if (stream._readableState && !stream._readableState.errored) {
stream._readableState.errored = er;
+ er.stack;
}
if (sync) {
diff --git a/lib/internal/streams/destroy.js b/lib/internal/streams/destroy.js
index 575b12f9c18b..483b6d11a196 100644
--- a/lib/internal/streams/destroy.js
+++ b/lib/internal/streams/destroy.js
@@ -25,6 +25,7 @@ function destroy(err, cb) {
}
if (err) {
+ err.stack;
if (w && !w.errored) {
w.errored = err;
}
@@ -61,6 +62,7 @@ function _destroy(self, err, cb) {
const w = self._writableState;
if (err) {
+ err.stack;
if (w && !w.errored) {
w.errored = err;
}
@@ -175,6 +177,7 @@ function errorOrDestroy(stream, err, sync) {
if ((r && r.autoDestroy) || (w && w.autoDestroy))
stream.destroy(err);
else if (err) {
+ err.stack;
if (w && !w.errored) {
w.errored = err;
} |
I'm good with this. Would be nice to do 4 regardless. |
@addaleax done |
Useful for future PR's to resolve situations where e.g. finished() is invoked on an already errored streams. PR-URL: #34103 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Useful for future PR's to resolve situations where e.g. finished() is invoked on an already errored streams. PR-URL: #34103 Backport-PR-URL: #34887 Refs: #34680 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Useful for future PR's to resolve situations where e.g. finished() is invoked on an already errored streams. PR-URL: #34103 Backport-PR-URL: #34887 Refs: #34680 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Notable changes: - buffer: also alias BigUInt methods (Anna Henningsen) #34960 - crypto: add randomInt function (Oli Lalonde) #34600 - perf_hooks: add idleTime and event loop util (Trevor Norris) #34938 - stream: simpler and faster Readable async iterator (Robert Nagy) #34035 - stream: save error in state (Robert Nagy) #34103 PR-URL: #35023
Notable changes: - buffer: also alias BigUInt methods (Anna Henningsen) #34960 - crypto: add randomInt function (Oli Lalonde) #34600 - perf_hooks: add idleTime and event loop util (Trevor Norris) #34938 - stream: simpler and faster Readable async iterator (Robert Nagy) #34035 - stream: save error in state (Robert Nagy) #34103 PR-URL: #35023 Conflicts: src/node_version.h
Notable changes: - buffer: also alias BigUInt methods (Anna Henningsen) nodejs#34960 - crypto: add randomInt function (Oli Lalonde) nodejs#34600 - perf_hooks: add idleTime and event loop util (Trevor Norris) nodejs#34938 - stream: simpler and faster Readable async iterator (Robert Nagy) nodejs#34035 - stream: save error in state (Robert Nagy) nodejs#34103 PR-URL: nodejs#35023 Conflicts: src/node_version.h
|
There is a downside to the following, beyond the small performance penalty.
As implemented in: node/lib/internal/streams/destroy.js Line 35 in 8a41d9b If import {Writable} from 'node:stream'
const error = new Error('This failed')
const stream = new Writable()
stream.destroy(error)
stream.on('error', () => {})
error.message = `Additional info: ${error.message}`
console.log(error) // This does not print 'Additional info'
// Error: This failed
// at file:///...
// ...I opened an issue at #51715. |

Useful for future PR's to resolve situations where various API's are invoked on an already errored stream.
Extracted from #34035
Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passes