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

Change default readableStrategy HWM to 0? #777

Closed
ricea opened this issue Aug 25, 2017 · 3 comments · Fixed by #808
Closed

Change default readableStrategy HWM to 0? #777

ricea opened this issue Aug 25, 2017 · 3 comments · Fixed by #808

Comments

@ricea
Copy link
Collaborator

ricea commented Aug 25, 2017

Currently a TransformStream with the default strategies adds two chunks of buffering to any pipe. I'd like to reduce that as much as possible. We can't reduce the HWM of the writableStrategy to 0 because it would have permanent backpressure preventing the pipe from working. However, reducing the HWM of the readableStrategy to 0 will work.

Advantages:

  • Reduces the amount of "bufferbloat" induced by adding an extra transform to a pipe, making backpressure propagate faster.
  • Makes transform() be read-triggered by default, reducing unnecessary work. However, as soon as you pipe the readable to something transforms will start happening, so it doesn't actually make much difference.

Disadvantages:

  • Breaks a surprising number of tests. I haven't looked at it in detail, but I don't think any of the breakage is fundamental. The plus side is that this will identify a set of tests whose behaviour changes with backpressure, and then we can split them into with- and without- backpressure versions.
  • TransformStream ends up with a different default from ReadableStream, which may cause some surprise.

I've thought of three variations for how to treat the third argument of TransformStream:

  1. undefined gets converted to {highWaterMark: 0}. Anything else is passed to the ReadableStream constructor unchanged.
  2. undefined and empty objects get converted to {highWaterMark: 0}. Anything else is unchanged.
  3. undefined gets converted to {highWaterMark: 0}. Anything else that doesn't already have a highWaterMark property gets one added with value 0.

I think I prefer the first one.

@domenic
Copy link
Member

domenic commented Aug 25, 2017

+1 to changing this. The disadvantages seem fine. I don't think we need the same defaults for readable vs. transform since we already know the readable strategy is very nich for transform streams.

The mechanics of how to do this are harder, hmm. I'm no longer convinced we're doing the right thing for non-transform streams, actually :(. With fresh eyes, I think I would have preferred that only undefined for the whole argument triggers defaults, and if it's not undefined, you have to specify both size and highWaterMark, otherwise you get errors.

Right now instead, providing undefined is the same as providing {}, and we have piece-wise defaults for size and highWaterMark. Oh well. So, how do we be consistent with that?

I think we do { size, highWaterMark } = {}, and if highWaterMark is undefined, we change it to 0 before passing it to ReadableStream. So we don't necessarily pass the exact object through to the readable stream.

Practically I think this looks like your 3.

@ricea
Copy link
Collaborator Author

ricea commented Sep 1, 2017

I like your suggestion better so that is what I am working on.

I have run into an interesting problem with this test:

promise_test(() => {
  const ts = new TransformStream();

  const writer = ts.writable.getWriter();
  writer.close();

  return Promise.all([writer.closed, ts.readable.getReader().closed]);
}, 'TransformStream: by default, closing the writable closes the readable (when there are no queued writes)');

This test never completes with HWM 0 because the promise returned by TransformWriterDefaultSink's start() method never resolves. I think this is pretty clearly bad behaviour.

Strictly speaking, this has always been broken, but making the readable HWM default to 0 makes it a lot more obvious.

I am looking at revising the state machine so that we don't need to delay returning from start().

@ricea
Copy link
Collaborator Author

ricea commented Sep 1, 2017

PR #786 fixes the test in my last comment.

ricea added a commit to ricea/streams that referenced this issue Sep 25, 2017
The reduces "buffer bloat" in pipes. The most significant change is that
now be default transform() is called until something is read. In normal
use, TransformStream will be used in a pipe and a transform() will be
triggered immediately to fill the queue in the following writable. So the
difference from the point of view of everyday use is minimal.

If the highWaterMark passed to the TransformStream constructor is
undefined (or not present) it will be changed to zero before passing it
on to the ReadableStream constructor.

Some tests assumed that transform() would be called without anything
being read and so have been fixed. In most cases the tests were fixed by
supplying an explicit highWaterMark to the constructor.

Verify that default strategies have the expected values.

Closes whatwg#777.
domenic pushed a commit that referenced this issue Sep 26, 2017
This reduces "buffer bloat" in pipes. The most significant change is that
now by default transform() is called until something is read. In normal
use, TransformStream will be used in a pipe and a transform() will be
triggered immediately to fill the queue in the following writable. So the
difference from the point of view of everyday use is minimal.

If the highWaterMark passed to the TransformStream constructor is
undefined (or not present) it will be changed to zero before passing it
on to the ReadableStream constructor.

Some tests assumed that transform() would be called without anything
being read and so have been fixed. In most cases the tests were fixed by
supplying an explicit highWaterMark to the constructor.

Verify that default strategies have the expected values.

Closes #777.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

2 participants