Uh oh!
There was an error while loading. Please reload this page.
stream: invoke buffered write callbacks on error - #30596
Conversation
Uh oh!
There was an error while loading. Please reload this page.
ronag
commented
Nov 22, 2019
Added comment and fixed linting |
mcollina
commented
Nov 22, 2019
@ronag Is this based on a semver-major commit that landed in 13? Are the above labels correct? |
ronag
commented
Nov 22, 2019
ronag
commented
Nov 22, 2019
I'll try to advise on those labels in future PR's. |
lpinca
commented
Nov 22, 2019
Are you sure this is a good idea? Technically those writes never happened so why their callbacks are invoked? I noticed that this behavior was introduced in #29028. I missed it. |
@lpinca: Not invoking callbacks in a callback based API is in my opinon very strange and could lead to deadlocks. e.g. this could deadlock... asyncfunctionwritePacket(){dst.write('header')for(constdataofsrc){awaitnewPromise((resolve,reject)=>dst.write(data,err=>err ? reject(err) : resolve())}}Not saying that's the best way to do that, but it should still be valid. |
Invoking all of them synchronously with an error that say "Cannot call write after a stream was destroyed" is not much better imho because:
|
Well in this case the error basically means and says that it wasn't attempted... no? In a callback based API if the operation is not attempted you still call the callback with something like If we promisified the API. Would the returned promise possibly never resolve nor reject in your opinion? |
lpinca
commented
Nov 22, 2019
Hmm I don't buy it in this case because the actual failure is already signaled on the stream and all writes are bound to it, so it's obvious that if it is no longer writable no more writes will be done. No writes attempted no callbacks. I preferred previous behavior but I guess it's too late :) |
mcollina
commented
Nov 22, 2019
If you call a function with a calback, the expectation throughout our codebase is that the callback is called. |
Is it? Here is one example where the callback is not called regardless of this change. const{ createDeflateRaw }=require('zlib');const{ randomBytes }=require('crypto');constdeflate=createDeflateRaw();deflate.resume();deflate.write(randomBytes(1024));deflate.flush(function(){thrownewError('Unexpected callback invocation');});process.nextTick(function(){deflate.close();}); |
ronag
commented
Nov 23, 2019
We should probably fix that as well... |
lpinca
commented
Nov 23, 2019
And many more, I guess. |
Uh oh!
There was an error while loading. Please reload this page.
Sorry to pester but there is one more thing which makes me very uncomfortable with this behavior. const{ Writable }=require('stream');functioncallback1(err){console.log('callback 1',err);}functioncallback2(err){console.log('callback 2',err);}constchunk=Buffer.alloc(1024);constwritable=newWritable({write(chunk,encoding,callback){setTimeout(callback,1000);}});writable.on('close',function(){console.log('close');});writable.write(chunk,callback1);writable.write(chunk,callback2);setTimeout(function(){writable.destroy();},500);Callbacks of queued writes can be called before a callback (or possibly callbacks in case of |
ronag
commented
Nov 23, 2019
No worries.
I'm not sure I follow? The write callback is called first and then any queued callbacks. Order is maintained? Do you think you can write a failing test I can look into? |
@lpinca: I think the behaviour you are referring to is what's done in #29028, not something this PR changes? If so I would propose we take that as a separate issue. I'm not sure whether |
lpinca
commented
Nov 23, 2019
Yes correct, too bad I missed that PR. Anyway I think it is relevant here too because this patch extends that behavior to streams that do not use |
Yes, but it happens in @lpinca: If you create an issue and assign it to me I will try to resolve your concern in a follow up PR to this. |
1bef798 to
7dec5bbCompareronag
commented
Nov 23, 2019
The state.length change broke this PR. Investigating. |
@lpinca Doesn't seem to much of a trouble to fix. I've included a fix commit for your concern in this PR. I'll move it to a separate PR if that's preferable. |
nodejs-github-bot
commented
Dec 17, 2019
nodejs-github-bot
commented
Dec 25, 2019
nodejs-github-bot
commented
Dec 25, 2019
nodejs-github-bot
commented
Dec 25, 2019
Buffered write callbacks were only invoked upon error if `autoDestroy` was invoked. PR-URL: #30596 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
Trott
commented
Dec 31, 2019
Landed in e66c4de |
BridgeAR
commented
Jan 3, 2020
This does not land cleanly on v13.x. Please open a manual backport in case this should be backported or leave a comment to update the labels in case it should not be backported. |
Buffered write callbacks were only invoked upon error if `autoDestroy` was invoked. Backport-PR-URL: nodejs#31179 PR-URL: nodejs#30596 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
ronag
commented
Jan 3, 2020
Done, #31179 |
Refs: #30596 Buffered write callbacks were only invoked upon error if `autoDestroy` was invoked. Backport-PR-URL: #31179 PR-URL: #30596 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
If the socket is closed while data is being compressed, invoke the current send callback and the buffered send callbacks with an error before emitting the `'close'` event. Refs: nodejs/node#30596
If the socket is closed while data is being compressed, invoke the current send callback and the buffered send callbacks with an error before emitting the `'close'` event. Refs: nodejs/node#30596
Buffered write callbacks were only invoked upon error if
autoDestroyis enabled.Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passes