Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[streams] Releasing write() callbacks on error #1746

Closed
mcollina opened this issue May 20, 2015 · 6 comments
Closed

[streams] Releasing write() callbacks on error #1746

mcollina opened this issue May 20, 2015 · 6 comments
Labels
stream Issues and PRs related to the stream subsystem.

Comments

@mcollina
Copy link
Member

In case the streams errors, e.g. for a broken TCP socket, or an application error, all buffered write callbacks are never called, here is an example:

var Writable = require('stream').Writable
var inherits = require('util').inherits

function MyWritable (opts) {
  Writable.call(this, opts)
}

inherits(MyWritable, Writable)

MyWritable.prototype._write = function (chunk, enc, done) {
  console.log('_write', chunk.toString())
  setTimeout(function () {
    done(new Error('faulty one'))
  }, 500)
}

var stream = new MyWritable()

stream.write(new Buffer('Hello First'), 'utf8', print('hello'))
stream.write(new Buffer('Hello Second'), 'utf8', print('second'))

stream.on('error', function (err) {
  console.log('error received', err)
})

function print (value, err) {
  return function () {
    console.log('done', value, err)
  }
}

Here is the output of the above program:

_write Hello First
done hello [Error: faulty one]
error received [Error: faulty one]

The code I am using to fix this behavior is https://github.com/mcollina/aedes/blob/master/lib/client.js#L87-L91.

I am ok to submit a PR about this issue, if you think it is worth fixing.

cc @mafintosh

@mcollina mcollina changed the title Releasing write() callbacks on error [streams] Releasing write() callbacks on error May 20, 2015
@Fishrock123 Fishrock123 added the stream Issues and PRs related to the stream subsystem. label May 20, 2015
@mcollina
Copy link
Member Author

this seems relevant, but it's related to the Readable interface nodejs/node-v0.x-archive#5920, originally reported by @kanongil.

also cc @chrisdickinson

@chrisdickinson
Copy link
Contributor

@mcollina: I have a bit of trepidation about this change, since it's hard to estimate the amount of breakage this would cause in the ecosystem (in that we go from "only calling one callback" to "calling all callbacks in the queue".) This should only be a problem for folks who are manually calling .write, though, so it's hard to say whether this is innocuous or not! One approach would be to introduce it as a new option for writable streams – a errorBehavior: CallOne | CallAll | Discard option that defaults to "CallOne" but may be overridden by users.

@kanongil: this seems to address a different issue than nodejs/node-v0.x-archive#5920 – which was looking for a way to "save" an error for the end of the stream.

@mcollina
Copy link
Member Author

@chrisdickinson That's the same though I had, but I thought that we might solve some leaks/bugs in other people code. I'm ok with a backward-compatible fix, as it is the safest bet. Basically this is an issue for everybody using stream without piping them, which is really advanced anyway (and I am only doing it for performance reasons). I'm not sure about the 'discard' option though, but it fits. If it's ok, then I will send a PR and we can discuss it there.

Regarding the breakage, we might get this is as a backward compatible thing, and maybe switch when a major release gets out. However, we should test this with some popular modules, just to see if it has any impact.

Is there a specific reason why it's 'CallOne' and not 'callOne'? I would have picked the latter if asked.

kevinoid added a commit to kevinoid/node that referenced this issue Jan 22, 2016
The current documentation for writable.end only specifies that the
callback is called "once the data has been fully handled".  It is
ambiguous whether this means "successfully handled" and, if so, whether
the callback is called if the data can not be successfully handled (i.e.
an error occurs).

The ambiguity is not only in the documentation.  The stream class
implementations differ on this point.  stream.Writable invokes the
callback with any errors that occur during parameter checking or during
calls to _write.  However, not all classes return all errors to _write.
zlib.Zlib does pass argument and state errors to the _write (_transform)
callback, but does not pass data errors.  http.OutgoingMessage passes
argument type errors and some other types of errors, but not all.

This inconsistency is behind issue nodejs#1746 and, I suspect, other issues in
client code which passes a callback to write.

This commit takes no position on whether the callback error behavior
should changed, but simply attempts to document the current behavior in
a way that is open to changes so that users are not caught by surprise.

Signed-off-by: Kevin Locke <[email protected]>
@Trott
Copy link
Member

Trott commented Mar 10, 2016

Is this being worked on/discussed elsewhere? (Trying to figure out if this needs to be closed or updated or anything. Looks like the basic situation may not have changed much so probably neither of those?)

@mcollina
Copy link
Member Author

@Trott neither of those. I think this should be part of the discussion in how to improve error handling in streams.

What we can do today is to fix the doc https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback and report that the callback might not be called at all if the stream gets destroyed before the data could be flushed. On top of that, we are missing a clear explanation of what .destroy() does anyway.

cc @calvinmetcalf

benjamingr pushed a commit that referenced this issue Mar 14, 2016
The current documentation for writable.write only specifies that the
callback is called "once the data has been fully handled".  It is
ambiguous whether this means "successfully handled" and, if so,
whether the callback is called if the data can not be successfully
handled (i.e. an error occurs).

The ambiguity is not only in the documentation.  The stream class
implementations differ on this point.  stream.Writable invokes the
callback with any errors that occur during parameter checking or
during calls to _write.  However, not all classes return all errors
to _write. zlib.Zlib does pass argument and state errors to the
_write (_transform) callback, but does not pass data errors.
http.OutgoingMessage passes argument type errors and some other types
of errors, but not all.

This inconsistency is behind issue #1746 and, I suspect, other issues
in client code which passes a callback to write.

This commit takes no position on whether the callback error behavior
should changed, but simply attempts to document the current behavior
in a way that is open to changes so that users are not caught by
surprise.

PR-URL: #4810
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Jeremy Whitlock <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
benjamingr pushed a commit that referenced this issue Mar 14, 2016
The current documentation for writable.write only specifies that the
callback is called "once the data has been fully handled".  It is
ambiguous whether this means "successfully handled" and, if so,
whether the callback is called if the data can not be successfully
handled (i.e. an error occurs).

The ambiguity is not only in the documentation.  The stream class
implementations differ on this point.  stream.Writable invokes the
callback with any errors that occur during parameter checking or
during calls to _write.  However, not all classes return all errors
to _write. zlib.Zlib does pass argument and state errors to the
_write (_transform) callback, but does not pass data errors.
http.OutgoingMessage passes argument type errors and some other types
of errors, but not all.

This inconsistency is behind issue #1746 and, I suspect, other issues
in client code which passes a callback to write.

This commit takes no position on whether the callback error behavior
should changed, but simply attempts to document the current behavior
in a way that is open to changes so that users are not caught by
surprise.

PR-URL: #4810
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Jeremy Whitlock <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
evanlucas pushed a commit that referenced this issue Mar 14, 2016
The current documentation for writable.write only specifies that the
callback is called "once the data has been fully handled".  It is
ambiguous whether this means "successfully handled" and, if so,
whether the callback is called if the data can not be successfully
handled (i.e. an error occurs).

The ambiguity is not only in the documentation.  The stream class
implementations differ on this point.  stream.Writable invokes the
callback with any errors that occur during parameter checking or
during calls to _write.  However, not all classes return all errors
to _write. zlib.Zlib does pass argument and state errors to the
_write (_transform) callback, but does not pass data errors.
http.OutgoingMessage passes argument type errors and some other types
of errors, but not all.

This inconsistency is behind issue #1746 and, I suspect, other issues
in client code which passes a callback to write.

This commit takes no position on whether the callback error behavior
should changed, but simply attempts to document the current behavior
in a way that is open to changes so that users are not caught by
surprise.

PR-URL: #4810
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Jeremy Whitlock <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
rvagg pushed a commit that referenced this issue Mar 16, 2016
The current documentation for writable.write only specifies that the
callback is called "once the data has been fully handled".  It is
ambiguous whether this means "successfully handled" and, if so,
whether the callback is called if the data can not be successfully
handled (i.e. an error occurs).

The ambiguity is not only in the documentation.  The stream class
implementations differ on this point.  stream.Writable invokes the
callback with any errors that occur during parameter checking or
during calls to _write.  However, not all classes return all errors
to _write. zlib.Zlib does pass argument and state errors to the
_write (_transform) callback, but does not pass data errors.
http.OutgoingMessage passes argument type errors and some other types
of errors, but not all.

This inconsistency is behind issue #1746 and, I suspect, other issues
in client code which passes a callback to write.

This commit takes no position on whether the callback error behavior
should changed, but simply attempts to document the current behavior
in a way that is open to changes so that users are not caught by
surprise.

PR-URL: #4810
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Jeremy Whitlock <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
MylesBorins pushed a commit that referenced this issue Mar 17, 2016
The current documentation for writable.write only specifies that the
callback is called "once the data has been fully handled".  It is
ambiguous whether this means "successfully handled" and, if so,
whether the callback is called if the data can not be successfully
handled (i.e. an error occurs).

The ambiguity is not only in the documentation.  The stream class
implementations differ on this point.  stream.Writable invokes the
callback with any errors that occur during parameter checking or
during calls to _write.  However, not all classes return all errors
to _write. zlib.Zlib does pass argument and state errors to the
_write (_transform) callback, but does not pass data errors.
http.OutgoingMessage passes argument type errors and some other types
of errors, but not all.

This inconsistency is behind issue #1746 and, I suspect, other issues
in client code which passes a callback to write.

This commit takes no position on whether the callback error behavior
should changed, but simply attempts to document the current behavior
in a way that is open to changes so that users are not caught by
surprise.

PR-URL: #4810
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Jeremy Whitlock <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
MylesBorins pushed a commit that referenced this issue Mar 21, 2016
The current documentation for writable.write only specifies that the
callback is called "once the data has been fully handled".  It is
ambiguous whether this means "successfully handled" and, if so,
whether the callback is called if the data can not be successfully
handled (i.e. an error occurs).

The ambiguity is not only in the documentation.  The stream class
implementations differ on this point.  stream.Writable invokes the
callback with any errors that occur during parameter checking or
during calls to _write.  However, not all classes return all errors
to _write. zlib.Zlib does pass argument and state errors to the
_write (_transform) callback, but does not pass data errors.
http.OutgoingMessage passes argument type errors and some other types
of errors, but not all.

This inconsistency is behind issue #1746 and, I suspect, other issues
in client code which passes a callback to write.

This commit takes no position on whether the callback error behavior
should changed, but simply attempts to document the current behavior
in a way that is open to changes so that users are not caught by
surprise.

PR-URL: #4810
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Jeremy Whitlock <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
@mcollina
Copy link
Member Author

mcollina commented Jun 6, 2017

Closing this. It is pretty old, and probably it's might cause too much breakage to be worth fixing.

@mcollina mcollina closed this as completed Jun 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stream Issues and PRs related to the stream subsystem.
Projects
None yet
Development

No branches or pull requests

4 participants