-
Notifications
You must be signed in to change notification settings - Fork 7.3k
stream: writes can return false but forget to emit drain #6506
Comments
I believe the issue is in It looks like previously the code did the right thing by only setting the flag to true, never resetting to false: https://github.com/meteor/node/blob/v0.9.10/lib/_stream_writable.js#L162, changed in 049903e in what looks to be an unrelated refactoring. |
By the way, the reason that this only affects piping from 0.8-style streams and not from 0.10-style streams (or, well, people doing their own flow control implementation) is that But I think it actually can occur just using 0.10-style streams and Let's say that But since there's no event associated with |
Yeah, this shows how the bug affects you even with only 0.10-style streams and If you run this with Without var assert = require('assert');
var util = require('util');
var stream = require('stream');
function PassThrough () {
stream.Transform.call(this);
};
util.inherits(PassThrough, stream.Transform);
PassThrough.prototype._transform = function (chunk, encoding, done) {
this.push(chunk);
done();
};
var s1 = new PassThrough;
s1.pipe(process.stdout);
var s2 = new PassThrough;
s2.pipe(process.stdout, {end: false});
var big = new Buffer(process.env.UNBREAK ? 16 : 16500);
big.fill('x');
big.write('\n', big.length - 1);
s1.write(big);
s2.write("tiny\n");
setTimeout(function () {
s1.write("later\n");
}, 10); |
I'm suffering from this issue when using node-mysql with |
Thanks @ayanamist and @indutny ! Looks like your PR fixed this issue. |
If a write is above the highWaterMark, _write still manages to fully send it synchronously, _writableState.length will be adjusted down to 0 synchronously with the write returning false, but 'drain' will not be emitted until process.nextTick. If another small write which is below highWaterMark is issued before process.nextTick happens, _writableState.needDrain will be reset to false, and the drain event will never be fired. So we should check needDrain before setting it up, which prevents it from inproperly resetting to false.
If a write is above the high water mark but _write still manages to
fully send it synchronously, _writableState.length can be adjusted down
to 0 synchronously with the write that returns false, but 'drain' will
not be emitted until process.nextTick.
If you then issue another write before process.nextTick happens which is short,
_writableState.needDrain
will be reset to false, and the drain event will never fire.Making matters worse, if you have a 0.8-style Stream piped into this writable stream, this means the source stream will get paused (due to a write returning false) and never resumed.
Eg, you would expect the following code to exit 0, not fail. It exits 0 if either of the two write calls are removed; you need the big one followed by the short one for the issue to occur.
The text was updated successfully, but these errors were encountered: