w3c / webtransport

WebTransport is a web API for flexible data transport
https://w3c.github.io/webtransport/
Other
820 stars 51 forks source link

Client initiated drain #436

Open wilaw opened 1 year ago

wilaw commented 1 year ago

As noted in #432 , per IETF a client may send a DRAIN_WEBTRANSPORT_SESSION signal to a server, to indicate that the server SHOULD NOT initiate new WebTransport streams or datagram flows.

Do we need a wt.drain() method to initiate this signal?

Would write() and stream creation methods work after drain() has been called?

aboba commented 1 year ago

It might be helpful to have a concrete use case that cannot be satisfied by the existing API.

Within the existing API, the client can stop accepting incoming undirectional or bidirectional steams, while continuing to read from streams that were previously accepted. In the IETF issue, there is a question about whether a drain request implies that the server should stop sending datagrams or just stop creating "new work" (however, the server defines that).

It is also possible for an application to define its own messages. For example, an RTP over WebTransport implementation could use the PAUSE indication defined in RFC 7728 to request that its peer pause the sending of an RTP stream, which could be transported in unidirectional stream(s) or datagrams.

wilaw commented 1 year ago

With the existing API, the client can stop accepting incoming streams. However it cannot stop the server from sending data across existing streams. The client can close() the existing streams of course, but then it risks losing data in flight.

aboba commented 1 year ago

This sounds like a "graceful shutdown" use case, where the client is asking the server to finish the existing work but not initiate new work, so that the client can take care of the data in flight (e.g. write it to stable storage) and then close the session.

It does lead me to wonder how quickly the server is expected to wrap things up. Should the server stop sending immediately, or can it continue to a logical break point (e.g. finish sending the current video, but don't keep sending more videos after that)?

wilaw commented 1 year ago

The IETF definition is intentionally vague on the timing question, with a SHOULD to "gracefully terminate the session as quickly as possible".

To drain a WebTransport session, either endpoint can send a DRAIN_WEBTRANSPORT_SESSION capsule. An endpoint SHOULD NOT initiate new WebTransport streams or datagram flows after sending or receiving a DRAIN_WEBTRANSPORT_SESSION capsule. Both endpoints can continue sending data on existing streams and datagram flows, but SHOULD attempt to gracefully terminate the session as quickly as possible.

We could use similar language and guidance if we do want to implement a drain() method.

LPardue commented 1 year ago

I think a lot of this is likely going to be application or deployment specific. So staying vague, or giving non-exhaustive examples, is the appropriate way to phrase it.

While rejecting new things is always possible. Draining helps both sides avoid pointless work. And as per the IETF issue https://github.com/ietf-wg-webtrans/draft-ietf-webtrans-http3/issues/27 it can be used by intermediaries that are not application aware if they have operational needs.

aboba commented 1 year ago

@vasilvv Do you have an opinion on this?

jan-ivar commented 1 year ago

Meeting:

wilaw commented 1 year ago

At IETF 116, WebTransport meeting, room feedback was to allow client initiated drain.

jan-ivar commented 8 months ago

Some questions:

vasilvv commented 8 months ago

Is this just a synchronous drain() method that sends the capsule each time?

We only handle incoming DRAIN capsule once, so it would make to send one only once. We could make it return a promise, but I am not sure what users would do with it.

Is the method idempotent or throw if called again?

Idempotent should have better ergonomics.

Should we add a new state called "draining"?

Unlike HTTP, we don't really alter connection behavior upon draining signal, so I'm not sure that's necessary.

Should this "draining" state block new stream creation?

HTTP does that. I think we should avoid that, since that makes some assumptions about how the application works that are not necessary.

jan-ivar commented 5 months ago

This seems like something that would be nice but not necessary, but It exists in IETF so we should probably expose it.

jan-ivar commented 5 months ago

We only handle incoming DRAIN capsule once, so it would make to send one only once. ...

If DRAIN is only sent once, what guarantee is there that the server received it?

jan-ivar commented 4 months ago

Meeting:

jan-ivar commented 1 month ago

Based on https://github.com/w3c/webtransport/issues/436#issuecomment-1325589571 and _"either endpoint can send a DRAIN_WEBTRANSPORTSESSION capsule" the likely API need here is something like:

wt.drain(); // void, no promise
await wt.draining; // will resolve in the next task
// we are now in "draining" state.

...probably with something like:

@nidhijaju do you have cycles to add something like this? Or we can reassign.

ekinnear commented 2 weeks ago

Should we add a new state called "draining"?

Unlike HTTP, we don't really alter connection behavior upon draining signal, so I'm not sure that's necessary.

Should this "draining" state block new stream creation?

HTTP does that. I think we should avoid that, since that makes some assumptions about how the application works that are not necessary.

It feels weird that we don't block new stream creation upon draining. I know in the IETF spec we said this was "up to the application", which I guess is fine, but did we mean the website itself or did we mean the browser/W3C spec here?

If we mean the actual website itself, why can't it just send its own message to signal draining, i.e. why specify a capsule/API at all?

aboba commented 2 weeks ago

It feels weird that we don't block new stream creation upon draining. I know in the IETF spec we said this was "up to the application", which I guess is fine, but did we mean the website itself or did we mean the browser/W3C spec here?

IMHO, "application" refers to both the client's Javascript application as well as server-side code, for either the server -> client or client -> server cases.

If we mean the actual website itself, why can't it just send its own message to signal draining, i.e. why specify a capsule/API at all?

The application can send its own message, so it's a good question. The stream that the message would be sent over will differ but does that make much difference?

Perhaps this is about convenience, saving the JS client application from needing to write their own drain() method, and allowing a node application to react to receipt of DRAIN_WEBTRANSPORT_SESSION ?

martinthomson commented 2 weeks ago

I'm with @ekinnear here. HTTP has a GOAWAY frame because HTTP is an application protocol and people use HTTP directly. However, we didn't add that capability to QUIC, so why would we add it here? (CONNECTION_CLOSE is not a graceful shutdown mechanism, it's an abrupt termination signaling mechanism, for error conditions.)

That line of thinking is making me less than enthusiastic about DRAIN_WEBTRANSPORT_SESSION. If the intermediate stack layers (the browser) can't act on the signal in a meaningful way, then it's not a useful signal at that layer.

aboba commented 2 weeks ago

Opened https://github.com/ietf-wg-webtrans/draft-ietf-webtrans-http3/issues/167

jan-ivar commented 5 days ago

Meeting: