Uh oh!
There was an error while loading. Please reload this page.
stream: save error in state - #34103
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.jsAssertionError[ERR_ASSERTION]: 0<256atTLSSocket.connect(/Users/ronagy/GitHub/nxtedition/node/test/parallel/test-gc-tls-external-memory.js:36:5)atTLSSocket.<anonymous>(/Users/ronagy/GitHub/nxtedition/node/test/common/index.js:365:15)atTLSSocket.emit(events.js:314:20)atemitErrorNT(internal/streams/destroy.js:127:8)atemitErrorCloseNT(internal/streams/destroy.js:92:3)atprocessTicksAndRejections(internal/process/task_queues.js:80:21)atrunNextTicks(internal/process/task_queues.js:62:3)atprocessImmediate(internal/timers.js:435:9){generatedMessage: false,code: 'ERR_ASSERTION',actual: false,expected: true,operator: '=='}Seems like a GC related bug in |
ronag
commented
Jun 29, 2020
@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. |
ronag
commented
Jun 29, 2020
ronag
commented
Jun 29, 2020
This is blocking #34035 |
lundibundi
commented
Jun 29, 2020
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. |
ronag
commented
Jun 29, 2020
That seems to have fixed it. I'd just like a thumbs up from @addaleax before landing this. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
lpinca
commented
Jun 30, 2020
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)? |
addaleax
commented
Jun 30, 2020
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;
} |
ronag
commented
Jun 30, 2020
I'm good with this. Would be nice to do 4 regardless. |
nodejs-github-bot
commented
Jun 30, 2020
ronag
commented
Jun 30, 2020
ronag
commented
Jun 30, 2020
@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'consterror=newError('This failed')conststream=newWritable()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