Re: END_SEGMENT and END_STREAM redundant

On 2014–04–21, at 4:34 AM, Roberto Peon <grmocg@gmail.com> wrote:

> FYI, you did't reply to the list, and you probably should :)

Argh, I’m always forgetting to “reply all”. I’ll just not delete anything from the quoted text here. My only response is at the very end.


> On Sun, Apr 20, 2014 at 8:39 AM, David Krauss <potswa@gmail.com> wrote:
> 
> On 2014–04–20, at 7:27 PM, Roberto Peon <grmocg@gmail.com> wrote:
> 
>> You're ascribing a semantic that I'm not thinking is the semantic of the protocol.
>> The protocol ensures that any END_SEGMENT occurs at the same byte offset from the first byte of a stream as when it was created.
>> Similarly, the protocol ensures that HEADERS are at the same byte offset.
> 
> Okay, good to know. It should be clarified in the spec.
> 
>> That depends on how it is defined. As I state above, END_SEGMENT or HEADERS are always at the same byte offset in the datastream.
>> Frames otherwise have zero semantic meaning, as they can be broken up/coalesced at will.
> 
> Well, adjacent HEADERS also cannot be coalesced, regardless of END_SEGMENT, right? You mentioned a headers-only protocol.
> 
> true
>  
> 
>> They always mean the same thing. Frame-level details should not be surfaced at the application layer. HTTP2 is not a frame-oriented protocol. It is either message (END_SEGMENT) or bytestream. Frames are subordinate to either of these and form the building blocks for creating streams of messages.
> 
> That’s the gist of my question: what the application layer may access. If (and only if) these details are available to application layer protocols, they may be changed by an intermediary. It is an identical relationship because, as you said, the protocol is the grammar. HTTP/2 isn’t litigating application-level meaning.
> 
> Gotcha.
>  
> 
>> Given my suspicions about coalescing data across headers, right now I’m thinking that each HEADERS frame should start a new “message” and all of segmentation is redundant. Applications that want a sequence of data-only messages with no metadata can spend 8 bytes on an empty HEADERS frame. Header-only protocols see no overhead.
>> 
>> No.
> 
> Now it’s my turn to say we’re almost entirely “violently” agreeing :) . Headers cannot be coalesced, and they always have a fixed position in the data stream. The only thing left between your current intention and elimination of segmentation is representation of the “end message” metadata as just another header, or an optimized flag bit.
> 
> In any case, headers do implement segmentation semantics even without using the END_SEGMENT bit.
> 
> Of data in a data-stream, yes, but otherwise they currently do not (unless one declared some key-value which did) denote end of message (end_of_segment).
>  
> 
>> In any case, 8 bytes is only equal to the overhead of the extra DATA frame that any segmentation implicitly requires, and nothing compared to the total overhead of flushing which is also likely to happen. We shouldn’t sacrifice anything for the sake of 8 bytes.
>> 
>> The flag bits are there to be used, there is no sacrifice here :)
> 
> I’m concerned with interface complexity, not wire overhead or room for future flag bits.
> 
> Gotcha.
>  
> 
>> The earlier BNF was imprecise, and it might help the big picture to definitively record the application-level view.
>> 
>> The current spec:
>> 
>> stream:
>> 	header-block segment* unterminated-segment? (end-stream|rst-stream)
>> 
>> segment:
>> 	unterminated-segment (headers-end-segment | data-end-segment)
>> 
>> unterminated-segment:
>> 	header-block* data-octet*
>> (Transport may move the headers relative to the data, such that their order within a segment is insignificant.)
>> 
>> Applications should never see frames. They should probably get things like:  Got headers on the stream. Got bytes on the stream. Got end of message. Got end of stream.
> 
> Agreed, although the above grammar does not have any frames (aside from rst-stream). It condenses what the stream protocol is guaranteed to transport from one end to the other. (Except for ordering of headers and data-octets, which I was unaware of.)
> 
>> What we get by fixing the location of all header blocks in the data stream, sacrificing multiple header blocks within a segment (replaceable by a user-defined x-begin-message header), and adding 8 bytes per segment that doesn’t start with headers:
>> 
>> stream:
>> 	segment+ (end-stream|rst-stream)
>> 
>> segment:
>> 	header-block data-octet*
>> 
>> I think this better matches what application designers expect. I didn’t include use of END_SEGMENT as an abnormal termination indicator in the list of sacrifices, because RST_STREAM already does that.
>> 
>> Application designers should never see the frame-level stuff. If they're ascribing semantic value to the frames, they're doing it wrong
> 
> OK. That needs to be specified. Otherwise, they have no reason not to do so.
> 
> Good point. IF that isn't clear it *really* needs to be clear, else we will have interop problems (the spec explicitly allows for coalescing/breaking up frames).
>  
> 
>> and their application *will* break as it goes throug a proxy.
> 
> The reason I’ve been asking these crazy questions about coalescing is because I’m looking for a reason something might break when it goes through a proxy. But, I don’t see one, so headers-end-segment and data-end-segment are de-facto different application-level symbols.
> 
>> The application-layer grammar's atoms are: metadata, bytes, end-of-message, end-of-stream.
> 
> Isn’t it all much simpler if end-of-message is just another piece of metadata?
> 
> To prevent abuse by applications, the protocol needs to define some canonicalization which allows proxies to mangle “wrong” usage, and shuffle the headers-end-segment and data-end-segment symbols so they become indistinguishable.
> 
> One approach would be to define an end-segment header, and define the END_SEGMENT bit to be an optimized representation for it. Then an empty DATA frame or an empty HEADERS frame with END_SEGMENT set are both encoding the same thing, and proxies may forward either as the other. END_SEGMENT on a DATA frame encodes a header set with only the end-segment header, and END_SEGMENT on a non-empty HEADER frame adds it to the set.
> 
> The issue there is that then we're not presenting an arbitrary-metadata interface with HEADERS. It would be arbitrary metadata *except* the end-segment header, which seems annoying from an application-use point of view.

It’s only a framing-level difference. This one header may be encoded without a HEADERS frame. Vice-versa, an end-segment symbol may be encoded (validly, but inefficiently) without the END_SEGMENT bit as a normal header, unless there’s a special rule against doing so. Frames aren’t visible to the application, though, so this is just the sort of separation we want.

Application-level language bindings are strictly simplified by folding any special segmentation APIs into the general header handling. The complication is moved into into header encoding/decoding, where it’s an isolated special case. For the encoder, special handling is optional (although very desirable) unless the spec requires it.

Is there some property I’m missing that sets end-segment apart from (other) headers? Both require subsequent data to start a new DATA frame. Although that’s not directly visible to the application, it guarantees delivery at a particular byte offset. Both may also encourage a flush that might otherwise be deferred by too little buffered data. What else is there?

Received on Monday, 21 April 2014 03:32:28 UTC