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

Writable does not check if stream has been destroyed during _final and _write #39030

Open
ronag opened this issue Jun 14, 2021 · 20 comments
Open
Labels
good first issue Issues that are suitable for first-time contributors. semver-major PRs that contain breaking changes and should be released in the next major version. stream Issues and PRs related to the stream subsystem.

Comments

@ronag
Copy link
Member

ronag commented Jun 14, 2021

Not sure if this is a problem but I think we should at least add a comment in the code that this case has been considered.

@ronag ronag changed the title Writable does not check if stream has been destroyed after during _final and _write Writable does not check if stream has been destroyed during _final and _write Jun 14, 2021
@targos targos assigned targos and unassigned targos Aug 9, 2021
@targos targos added the stream Issues and PRs related to the stream subsystem. label Aug 9, 2021
@targos
Copy link
Member

targos commented Aug 9, 2021

@nodejs/streams

@mcollina
Copy link
Member

mcollina commented Aug 9, 2021

I don't know to be honest as I don't want to make things too stringent. However adding a check is going to improve the developer experience.

What should the check do? Throw? emit 'error'?

@ronag
Copy link
Member Author

ronag commented Aug 9, 2021

I think:

  1. cancel any further substeps
  2. if stream was destroyed without error, override with error

@mcollina mcollina added the semver-major PRs that contain breaking changes and should be released in the next major version. label Aug 9, 2021
@mcollina
Copy link
Member

mcollina commented Aug 9, 2021

Should we tag this as good first issue?

@ronag ronag added the good first issue Issues that are suitable for first-time contributors. label Aug 9, 2021
@mdaj06
Copy link

mdaj06 commented Aug 12, 2021

@ronag Can i take this up?

@mcollina
Copy link
Member

Go for it!

@Svanazar
Copy link

Svanazar commented Sep 3, 2021

Hey! I was wondering about the status of this issue, and if I could try looking into it?

@mcollina
Copy link
Member

mcollina commented Sep 5, 2021

Go for it!

@Svanazar
Copy link

Svanazar commented Sep 6, 2021

I went through Writable.js, but I'm not sure where should the checks be added. Specifically, I found these to be already present:

  • _write at line 321 checks for state.destroyed before going to the user-provided _write function
  • _final seems to be called through prefinish which also checks for state.destroyed at line 718

I'll really appreciate some guidance on this

@ronag
Copy link
Member Author

ronag commented Sep 7, 2021

You need to look at the time between those being called and their callback being invoked.

@Svanazar
Copy link

Thanks! I followed the code between these two times, and wanted to confirm the location. For _write, will it be appropriate to place the check inside onwrite() ?

This will be for the case when the stream gets destroyed during _write but no error is passed to the callback.

@rscherich
Copy link

After looking into this, this seems to run counter to a previous pull request (maybe I didn't understand something?): #24062
Of course, the project requirements might have changed since then too.

I can't immediately see a way to throw an error at the start of the callbacks without changing/removing several of the tests. I also foresee some issues with asynchronous implementations of _write and _final (also in the tests). Do we want to go ahead with the change anyway?

JPedroAmorim added a commit to JPedroAmorim/node that referenced this issue Dec 14, 2021
JPedroAmorim added a commit to JPedroAmorim/node that referenced this issue Dec 14, 2021
justmoon added a commit to interledgerjs/interledgerjs that referenced this issue May 1, 2022
These tests were dependent on Node.js streams error handling behavior which has changed. Namely, Writeable would previously throw an error when calling write on a closed stream.

The only thing that Node seems to do reliably across versions is return false if the write failed and pass the error to the write callback.

There is an open issue to once again throw an error for write-after-destroy (although not for write-after-end):

nodejs/node#39030

In Node 16 the behavior for write-after-{end|destroy} when there is no listener for the `error` event is to throw an uncaughtException. This is hard/janky to catch for test purposes and anyway only really tests Node's internal code, not our code. So I removed those tests and instead tightened up the tests for the case when there is an `error` handler registered.
justmoon added a commit to interledgerjs/interledgerjs that referenced this issue May 2, 2022
These tests were dependent on Node.js streams error handling behavior which has changed. Namely, Writeable would previously throw an error when calling write on a closed stream.

The only thing that Node seems to do reliably across versions is return false if the write failed and pass the error to the write callback.

There is an open issue to once again throw an error for write-after-destroy (although not for write-after-end):

nodejs/node#39030

In Node 16 the behavior for write-after-{end|destroy} when there is no listener for the `error` event is to throw an uncaughtException. This is hard/janky to catch for test purposes and anyway only really tests Node's internal code, not our code. So I removed those tests and instead tightened up the tests for the case when there is an `error` handler registered.
@djs113
Copy link

djs113 commented Aug 1, 2022

When I went through Writable.js I was unable to understand the state.sync flag, could anyone explain what it is?

@SebasQuirogaUCP
Copy link

Seems to be an interesting research topic.
Let me know and we arrange a meeting for discussing it.

@Viper-space
Copy link

Hey i was wondering if this issue is still open and if i can take a crack at it 😸

@zeazad-hub
Copy link

Hi, is this issue still open. If so, I can try and resolve it.

@zeazad-hub
Copy link

This would also be my first issue if I am able to work on it.

@zeazad-hub
Copy link

Can you assign this issue to me?

@Ceres6
Copy link
Contributor

Ceres6 commented Aug 13, 2023

Hi. Is this still open for people to work on?

If so is the following the expected behaviour?

I think:

  1. cancel any further substeps
  2. if stream was destroyed without error, override with error

@quixote15
Copy link

@mcollina @ronag I think that emitting ERR_STREAM_DESTROYED is the behavior this PR is trying to avoid #45062

I've dug into this issue, and I've got some insights to share. Take a look:

It seems that throwing ERR_STREAM_DESTROYED is the behavior the PR #45062 is attempting to avoid.. Also, in PR #25973, they tweaked the documentation on destroy. Now it implies that there might be cases where ERR_STREAM_DESTROYED won't be triggered.

Previously, it said:

After this call, the writable stream has ended and subsequent calls
to `write()` or `end()` will result in an `ERR_STREAM_DESTROYED` error.

Now:

This is a destructive and immediate way to destroy a stream. Previous calls to
`write()` may not have drained, and may trigger an `ERR_STREAM_DESTROYED` error.

Upon examining the code, it's apparent that the ERR_STREAM_DESTROYED error occurs when destroy is called while there is still data in the buffer awaiting write.

For example:

 const callbacks = [];
  const wb = new Writable({
    write(data, enc, cb) {
      callbacks.push(cb);
    },
    // Effectively disable the HWM to observe 'drain' events more easily.
    highWaterMark: 1
  });

  wb.write('abc', onWrite); 
  // Second write goes to buffer since highWaterMark===1
  wb.write('bbb', onWrite); // Throws ERR_STREAM_DESTROYED
  wb.destroy();
  callbacks.shift()();

So, based on what I've seen, I'm thinking we might not need to dive deeper into this issue. I would appreciate your thoughts on this.

Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Issues that are suitable for first-time contributors. semver-major PRs that contain breaking changes and should be released in the next major version. stream Issues and PRs related to the stream subsystem.
Projects
None yet