-
Notifications
You must be signed in to change notification settings - Fork 164
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
Transform streams and acknowledgement of writes #329
Comments
When one wants to create a chain to exchange operations, they choose to use the ack functionality carefully. If they don't, they can use one without the functionality. So, in case the consumer choose to use Regarding the buffer reusable |
This comment is for byte streams.
Let me define readers as:
Let me define writers as:
Is the below behavior reasonable?
|
It seems reasonable... I can't think of any other good behaviors at least. It's a bit surprising how BYOB becomes a footgun. I wonder if user-allocated buffer + has read ack would be better? If so maybe we should not have user-allocated buffer without ack at all? Hmm, but I wonder if MSE for example will never be done with the buffer and so would just never ack. Maybe that's OK? |
Yeah, both two writers above provide buffers, so they don't work well with BYOB which provides its buffer for reading. If there is a writer that takes a function which will be called with a buffer provided via BYOB reader, it will work well with BYOB reader (But I think it is a bad idea). Stating the reading way in the spec may be beneficial ("The fetch operation will read from the given stream with a |
In the
This also works well with BYOB-style reader side. The returned |
A row describing ideal behavior for the
|
Ownership movement
|
Let's also think about what ideal data transfer algorithms are for various combination of source and dest.
|
Note that beginWrite style writer cannot work well with push-style source. |
Observations
|
I guess we should clearly distinguish write-with-ack and write-and-forget interface by providing two different getters. Otherwise, we cannot determine whether the stream should return the given buffer or not. If it's not required to return it to the producer, the stream can pass it to the consumer ((a) in #329 (comment)). We should ask the producer to declare what he/she is expecting.
|
This is a great analysis @tyoshino. Thank you so much for digging in to it. The breakdown of default/borrowing/providing and default/byob/lending is really insightful. I am concerned about the ergonomics for authors, regarding choosing between all of these. But maybe we can hide that so that pipeTo and pipeThrough make the smart decisions automatically? Authors end up using getReader() and getWriter() almost all of the time, except sometimes they use getByobReader() when they want read(n)-type functionality. It feels a like we might be over-engineering... But it's also important to do acknowledgement and piping right... hmm. Hopefully we can do this in layers: ReadableStream -> ReadableStream with BYOB reader (to allow read(n) functionality if nothing else) -> full flexibility.
This works for more than just buffers though, right? Still lots to think on here. But I am hopeful we are on the right track... |
Yeah. At least for pipeTo, we can just implement the right thing to do.
I agree that people who want to use the most basic one shouldn't be bothered to understand the other extended ones. I need to explore more to realize it.
Interesting. Yeah, actually we're allowing read(n) by BYOB. We can think of a variant of read(n) family that does not take a buffer but read only up to n. And, we can easily think of that for ReadableStream.
Ah, yes. The transform stream waits for ack and if the producer side is using buffer-borrowing-writer, it forwards the ack signal. Not just a buffer. |
Merged (c) of #323 to #329 (comment). |
Here's the algorithm.
|
In PR #361, we can say that:
|
Spinning off from roughly yutakahirano/fetch-with-streams#30 (comment), I think we want to take a more general look at how we envision transform streams working.
Here is some code for reference:
In the current design,
onWritten
will always be called ASAP, whereasonWriteFailed
will never happen. (In the more general transform stream case,onWritten
will be called when the possibly-async transformation finishes, whileonWriteFailed
will be called if it fails.) The current design is based on how Node.js/io.js handles things.In the linked thread, @wanderview proposes (indirectly) a design where
onWritten
is called only after the chunk leaves the transform stream to head for the readable stream (i.e. after 1 second, roughly around the same time as (1)). In this designonWriteFailed
is also never called.@tyoshino's read-acknowledgment proposal (#324 and previous) would allow for a third design, where
onWritten
would be called oncedoSomethingAsyncWith(value)
succeeds, andonWriteFailed
would be called if it fails. This would require a slight modification to the above, e.g. something likewhich could be a burden on the consumer to remember to use.
There's two aspects to keep in mind with these proposals:
I think all can propagate backpressure pretty well, with minor tricks. But propagating write success/failure all the way from the ultimate sink to the original producer is only really possible with #324, I think. (Except perhaps through things like #325 which could help with special cases...)
The text was updated successfully, but these errors were encountered: