Re: Design Issue: Max Concurrent Streams Limit and Unidirectional Streams

I suspect that it is largely going to depend largely on the
compression mechanism we ultimately choose.

A client opens a new session with the server and sends it's first
HEADERS+PRIORITIES frame. The server will initialize the decompression
context for the headers, storing some bit of data. A while later, the
client closes the stream. Is the server required to maintain that
compression state for an indefinite period of time through the life of
the session or is there some reliable way of dumping the state because
you know it will no longer be used? The server can't really know if
the client is done sending requests using the same compression
context. The client could end up waiting quite a while before sending
a new HEADERS+PRIORITIES frame that uses the exact same compression
state. AFAICT none of the proposals on the table for header
compression really answer this question effectively.

If streams are allowed to linger in an indefinitely long half-open
state, then it becomes much more difficult to answer this question.
Consider.. The states I suggest are all take from the receiving
endpoints point of view... A stream in the Open, Reserved-Open, and
Reserved-Closed states means that the endpoint can expect to receive
new headers at some point; once the state changes to Closed, no new
headers on that stream are possible. That doesn't mean we can dump the
stored headers yet tho. Currently, we have absolutely no mechanism
with which to determine when it's ok to dump those stored headers.

On Fri, May 3, 2013 at 6:59 PM, William Chan (陈智昌)
<willchan@chromium.org> wrote:
> I think there's some missing context not being stated in the emails which is
> leading to confusion. Can we revisit why decoupling the stream directions
> will lead to a longer header lifetime? I would think that when the streaming
> layer encounters headers for a stream, it would hand it up to the HTTP
> semantic layer. As long as the HTTP semantic layer understands the
> request-response coupling, it knows to keep the request headers around until
> the response arrives, right?
>
>
> On Fri, May 3, 2013 at 10:49 PM, Roberto Peon <grmocg@gmail.com> wrote:
>>
>> Hey, you're the one worried about the size of the compressor state (which
>> would be ~4 or 8k)! :)
>> Headers are sometimes larger individually, and the upper limit to the size
>> of this state is the sum of 10s to 100s of these.
>>
>> I think that, pragmatically, since there is a framing-layer solution which
>> ensures that one must not store headers for longer that necessary, and which
>> is semantic-layer agnostic, it is a decent bet.
>> Any approach other than declaring it as unidirectional (or equivalent)
>> either requires the caching of much state, or a NACK from the remote side.
>>
>> That isn't to say that Martin's approach doesn't have appeal. I want to
>> like it, but unless we are to have unlimited streams in some limbo state, it
>> would require a NACK, else I won't be able to correlate sets of headers on a
>> stream and the NACK both requires more machinery at both ends, and consumes
>> more bytes on the wire.
>> -=R
>>
>>
>>
>> On Fri, May 3, 2013 at 6:19 PM, James M Snell <jasnell@gmail.com> wrote:
>>>
>>> Ok.. going back over the thread in detail and over the spec again, one
>>> approach to addressing the overall concern here (and hopefully bring a
>>> bit more rigor to the overall design) is to redefine the stream states
>>> slightly along the same lines already suggested by Martin. Each
>>> endpoint would maintain its own view of the current activity state of
>>> every stream in a session, however, the state would only reflect the
>>> actions taken by the peer endpoint. There are five possible activity
>>> states:
>>>
>>> Unused
>>>   The peer endpoint has not reserved or used the stream in any way.
>>>
>>> Open
>>>   The endpoint has received frames on the stream from the peer, none
>>> of which are type RST_STREAM or have the FINAL flag set.
>>>
>>> Closed
>>>   The endpoint has received an RST_STREAM frame, or any frame with the
>>> FINAL flag set from the peer
>>>
>>> Reserved-Open
>>>   The peer has reserved the stream identifier for future use but
>>> frames have not yet been received on that stream. The receiving
>>> endpoint is expected to send its own frames on the same stream.
>>>
>>> Reserved-Closed
>>>   The peer has reserved the stream identifier for future use but
>>> frames have not yet been received on that stream. The receiving
>>> endpoint is not expected to send its own frames on the same stream.
>>>
>>> MAX_CONCURRENT_STREAMS == The number of streams in the Open state the
>>> endpoint will permit the peer to initiate at any given time. Once that
>>> limit is reached, the receiving endpoint will likely begin rejecting
>>> new streams using RST_STREAM. In other words, right now,
>>> MAX_CONCURRENT_STREAMS is defined in terms of what the sending
>>> endpoint must not do. This changes the definition to an indication of
>>> what the receiving endpoint will do once a particular threshold is
>>> reached. Any endpoint that wants to be able to keep creating streams
>>> must be diligent about sending FINAL frames, etc.
>>>
>>> As for the Request-Response bounding issue, that's really an HTTP
>>> semantic layer notion. I'm not fully convinced we really need to
>>> handle that issue in the framing layer at all.
>>>
>>>
>>> On Fri, May 3, 2013 at 2:20 PM, Roberto Peon <grmocg@gmail.com> wrote:
>>> > The biggest rub in Martin's suggestion is that, as a stream initiator,
>>> > I no
>>> > longer know for how long I should keep the original "request" headers
>>> > around.
>>> > I view that as an annoying problem (I want every response to be
>>> > attributable
>>> > to a request).
>>> >
>>> > I also think it is a bit confusing-- how would it be used in cases
>>> > where
>>> > I've sent all my data on what I thought was a unidirectional stream,
>>> > and
>>> > then receive bytes from the other side on that stream. That'd be...
>>> > weird.
>>> >
>>> > With the unidirectional bit (or similar declaration of half-closed
>>> > start-state), I now know (by fiat, essentially) that I will not receive
>>> > a
>>> > response on that stream ID, and so I don't need to keep the "request"
>>> > headers around after I've finished pushing the stream. Logging
>>> > accomplished.
>>> >
>>> >
>>> > I think this is an easy issue to solve by reinstating the
>>> > unidirectional bit
>>> > (for now). It is certainly minimal work to have servers which do server
>>> > push
>>> > set that bit.
>>> >
>>> > To Will's point, I agree that an "ENHANCE YOUR CALM" code seems
>>> > redundant.
>>> > In my case I believe it redundant because the remote side has already
>>> > received my settings frame, or is sending without having known it (i.e.
>>> > within the initial RTT), and will be receiving the SETTINGS frame
>>> > before it
>>> > could process this new code anyway (assuming I'm following spec and
>>> > sending
>>> > SETTINGS immediately upon session establishment).
>>> > -=R
>>> >
>>> >
>>> >
>>> >
>>> >
>>> > On Fri, May 3, 2013 at 11:28 AM, William Chan (陈智昌)
>>> > <willchan@chromium.org>
>>> > wrote:
>>> >>
>>> >> I guess I kinda think that we're worrying too much about this corner
>>> >> of
>>> >> the spec. I don't view it as a big deal in practice. The problem
>>> >> described
>>> >> happens when MAX_CONCURRENT_STREAMS is too low to allow enough
>>> >> parallelism
>>> >> per roundtrip. I would advise people to simply increase their
>>> >> MAX_CONCURRENT_STREAMS in that case. I kinda think this is only
>>> >> problematic
>>> >> when we have very high latencies and devices that can't handle high
>>> >> parallelism, like an interplanetary refrigerator that speaks HTTP/2
>>> >> for some
>>> >> reason. <shrug>
>>> >>
>>> >> I am unsure how to feel about a ENHANCE YOUR CALM code as it's not
>>> >> well
>>> >> defined. I don't mind RST_STREAMs on exceeding limits, like the
>>> >> initial
>>> >> MAX_CONCURRENT_STREAMS, since they're usually the result of a race
>>> >> (the
>>> >> possible initial SETTINGS frame race) and we won't have to keep
>>> >> continually
>>> >> sending RST_STREAMs to rate limit appropriately.
>>> >>
>>> >>
>>> >> On Fri, May 3, 2013 at 3:02 PM, James M Snell <jasnell@gmail.com>
>>> >> wrote:
>>> >>>
>>> >>> The impact on client-to-server initiated streams is another reason
>>> >>> why
>>> >>> I suggested the credit-based approach and why it would likely be good
>>> >>> to have an RST_STREAM "ENHANCE YOUR CALM" error code [1]. If the
>>> >>> client misbehaves and sends too much too quickly, we have flow
>>> >>> control, settings, rst_stream and goaway options to deal with it.
>>> >>>
>>> >>> [1]
>>> >>>
>>> >>> http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#4xx_Server_Error
>>> >>>
>>> >>> On Fri, May 3, 2013 at 10:34 AM, William Chan (陈智昌)
>>> >>> <willchan@chromium.org> wrote:
>>> >>> > As I understand the proposal, which I believe ties into the issue
>>> >>> > James
>>> >>> > raised at the beginning here, the goal is to be able to open and
>>> >>> > close
>>> >>> > a
>>> >>> > directional stream without an ACK, which I am nervous about as I
>>> >>> > said
>>> >>> > above
>>> >>> > without much detail. Concretely speaking, a HTTP GET is a
>>> >>> > HEADERS+PRIORITY
>>> >>> > frame with a FINAL flag or an extra DATA frame with FINAL flag.
>>> >>> > This
>>> >>> > means
>>> >>> > that the request effectively never gets counted against the
>>> >>> > directional
>>> >>> > stream limit, as controlled by the receiver which sends a
>>> >>> > MAX_CONCURRENT_STREAMS setting, since it open and closes the
>>> >>> > direction
>>> >>> > in
>>> >>> > the same frame (or closes in the subsequent empty DATA frame).
>>> >>> >
>>> >>> >
>>> >>> > On Fri, May 3, 2013 at 1:52 PM, Martin Thomson
>>> >>> > <martin.thomson@gmail.com>
>>> >>> > wrote:
>>> >>> >>
>>> >>> >> On 3 May 2013 09:44, William Chan (陈智昌) <willchan@chromium.org>
>>> >>> >> wrote:
>>> >>> >> > I'd like server folks to chime in, but doing this makes me feel
>>> >>> >> > a
>>> >>> >> > bit
>>> >>> >> > nervous. I feel this effectively disables the directional
>>> >>> >> > concurrent
>>> >>> >> > streams
>>> >>> >> > limit. The bidirectional full-close essentially acts like an
>>> >>> >> > ACK, so
>>> >>> >> > removing it might result in an unbounded number of streams.
>>> >>> >>
>>> >>> >> I think that I know what you mean here, but can you try to expand
>>> >>> >> a
>>> >>> >> little?  Do you refer to the possible gap between close on the
>>> >>> >> initiating direction and the first frame on the responding
>>> >>> >> direction;
>>> >>> >> a gap that might cause the stream to escape accounting?  I think
>>> >>> >> that
>>> >>> >> is a tractable problem - any unbounded-ness is under the control
>>> >>> >> of
>>> >>> >> the initiating peer.
>>> >>> >
>>> >>> >
>>> >>
>>> >>
>>> >
>>
>>
>

Received on Saturday, 4 May 2013 02:42:35 UTC