Re: Unidirectional streams PR

Jana Iyengar <jri@google.com> Wed, 21 June 2017 23:48 UTC

Return-Path: <jri@google.com>
X-Original-To: quic@ietfa.amsl.com
Delivered-To: quic@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 531771200CF for <quic@ietfa.amsl.com>; Wed, 21 Jun 2017 16:48:11 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -2
X-Spam-Level:
X-Spam-Status: No, score=-2 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RP_MATCHES_RCVD=-0.001, SPF_PASS=-0.001, URIBL_BLOCKED=0.001] autolearn=ham autolearn_force=no
Authentication-Results: ietfa.amsl.com (amavisd-new); dkim=pass (2048-bit key) header.d=google.com
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 98o-OJ6brOX0 for <quic@ietfa.amsl.com>; Wed, 21 Jun 2017 16:48:08 -0700 (PDT)
Received: from mail-pg0-x234.google.com (mail-pg0-x234.google.com [IPv6:2607:f8b0:400e:c05::234]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 4343B12426E for <quic@ietf.org>; Wed, 21 Jun 2017 16:48:08 -0700 (PDT)
Received: by mail-pg0-x234.google.com with SMTP id f127so367868pgc.0 for <quic@ietf.org>; Wed, 21 Jun 2017 16:48:08 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=ySgRHAVhCxfTMsjnto1OIcgq7Yr2QnZ0ARB/W50hml8=; b=g6aDhCkfO98GilzX2ra2eO89DQAehomn0lUiOvJVKHpEOsNann3K5ZiVnZdps/10ta Nk8gTef2T4ymZf87BNBcn+OMFPVEdqpsqdZQVva6TNApBJ/+KGFTPeHetWFCWwJTZWVX UUMty7NXTgpX7u8z2wh+4WyM6y3CsCiP8J9/BCSQJ5sCimrCunBPSYMuG5a9Lish0w2d pqn86ZC9HBHjPH6AkxDho+fQ4XsO3JxpDIFr49IN55acORmGh426SE0VWcfbVHnKDyAs D8fMSevuugmLWdM1s+mVTt3tInr4Y7typFYavro7L29EBoimyOm+KXMiirnjfwHlyljh hCrA==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=ySgRHAVhCxfTMsjnto1OIcgq7Yr2QnZ0ARB/W50hml8=; b=LpezSQV1PiIGB5HeI8k78mdSzYPg8LSOl9Ktrt8RfO44GnzkLXH10RdDrcDRpLWsNB TfjVQ2dyh6c69pOGD0yHe5nxX/AdETl9LjiaCElnSgyX/QqhyZMUn676xmvqEEyYEO9g 27TaZVZFKp4j2LkTmOvmUnhayGvmJ0jN2C/Gl1zfc+Ic1fMkbk7x3tuNqCKamgCnvCeU D1+vCR7EQCjCgnyyQwssK+l3x5AJHTKQMuC+FesEoMwCGHglFdCClLFFEimKshfADyFP 4rCrpviEUn1DV/NPz4l45pxu5cEhHzDKkvZ3spb/g5CSUSpcB7gyf3lbSis238BOo1Ev KAGw==
X-Gm-Message-State: AKS2vOy/oMLMfNFi1w2WletOhh/PFPI5ClB462Vj7v1rwlSU1UD+TeqV gqtwLhFOYzZ48kBW8rRPMLCVBE8SJDgb
X-Received: by 10.99.122.13 with SMTP id v13mr27963740pgc.156.1498088887565; Wed, 21 Jun 2017 16:48:07 -0700 (PDT)
MIME-Version: 1.0
Received: by 10.100.179.39 with HTTP; Wed, 21 Jun 2017 16:48:07 -0700 (PDT)
In-Reply-To: <CAOdDvNpORYBr7+Q8M_nnGOm4MsWqVbm6koOtQ+=An8t7AbccGg@mail.gmail.com>
References: <CABkgnnW+veDVq27v+wTz0cA=eGPRTLQ1A90A0ynHLPU88Pg77Q@mail.gmail.com> <CAOdDvNpORYBr7+Q8M_nnGOm4MsWqVbm6koOtQ+=An8t7AbccGg@mail.gmail.com>
From: Jana Iyengar <jri@google.com>
Date: Wed, 21 Jun 2017 16:48:07 -0700
Message-ID: <CAGD1bZb9Na32z=Gg9JS+FzrGGN9Jhw=QDTMTYS=FVcNesoSMig@mail.gmail.com>
Subject: Re: Unidirectional streams PR
To: Patrick McManus <pmcmanus@mozilla.com>
Cc: Martin Thomson <martin.thomson@gmail.com>, QUIC WG <quic@ietf.org>
Content-Type: multipart/alternative; boundary="f403045c60fafcdf25055280ffb2"
Archived-At: <https://mailarchive.ietf.org/arch/msg/quic/6DtTCi-dEKUp7cJE3-efjTHv_k0>
X-BeenThere: quic@ietf.org
X-Mailman-Version: 2.1.22
Precedence: list
List-Id: Main mailing list of the IETF QUIC working group <quic.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/quic>, <mailto:quic-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/quic/>
List-Post: <mailto:quic@ietf.org>
List-Help: <mailto:quic-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/quic>, <mailto:quic-request@ietf.org?subject=subscribe>
X-List-Received-Date: Wed, 21 Jun 2017 23:48:11 -0000

Thanks for writing up the PR, Martin. It does help to see the details laid
down. I have a few thoughts.

First, there are issues that unidirectional streams raises, some of which
we can articulate, and some which we don't know yet simply because we have
no production experience with them.

In the former category, here's an issue that isn't handled right now.
Google maps uses map-tiles for displaying parts of mapped region. As a user
moves to a different region, the client cancels all pending requests, since
those map-tiles are no longer relevant and those responses are no longer
needed. Let's say that the requests were completely sent (FIN and all), but
the responses hadn't started yet. In the bidirectional world, a client
would achieve this by sending a RST_STREAM on all streams that have
responses pending, causing the server to send a RST_STREAM back. In the new
world, the client would have no way of canceling a pending request until
the server started responding. (And right now in your PR, I don't think
there's a way for an endpoint to RST a peer-initiated stream.)

Of the issues we don't know about yet, we'll only know with deployment
experience. I'm wary of standardizing something where we have no deployment
experience, no production experience. (Server Push is an overused but
classic example of this: it was standardized before there was any
significant deployment experience, and we continue to struggle with
actually deploying it. Also, see HTTP/2 Priorities.)

Second, your key premise is that unidirectional streams makes transport
logic simpler. I agree with that, and the PR clearly simplifies the
transport stream machinery. However, HTTP requires bidirectional streams,
and this PR moves that complexity to HTTP. This is useful if HTTP were the
only application that did request-response, but request-response is an
incredibly common design pattern, and one that is naturally facilitated by
bidirectional streams. Making every application reimplement bidirectional
streams atop QUIC's unidirectional streams *adds* complexity overall. My
design sense says to optimize for the common case and to allow for the
exceptions. Given that our scope is to really get things rolling for HTTP
ASAP, I'd argue that request-response is the common case that we need to
optimize for.

This PR makes it even clearer to me that we need deployment experience with
unidirectional streams before going down this path. We have that with
bidirectional streams, which makes it worth standardizing.

To be clear, I'm not saying that unidirectional streams aren't interesting.
FWIW, the original input draft
<https://www.ietf.org/archive/id/draft-hamilton-quic-transport-protocol-01.txt>,
which is still how GQUIC works right now, has app read_close and
write_close transitions in the stream state machine which would allow a
stream to become unidirectional, but the application endpoints have to know
that these streams are unidirectional. We can explicitly specify a separate
bit in the STREAM frame that indicates uni/bi-directional stream, allowing
for unidirectional streams as "always half-closed" (bidirectional) streams.
Implementations can obviously optimize code for uni streams separately, but
the spec would be straightforward. I'm happy to write this up as an
alternative, since I think this is valuable.

- jana



On Wed, Jun 21, 2017 at 2:48 AM, Patrick McManus <pmcmanus@mozilla.com>
wrote:

> I have come to strongly support unidirectional streams. I'm writing
> because I think we can take this just a tiny bit further and get a pretty
> big architectural win.
>
> the PR adds a stream-header to the http mapping.. a key feature of that is
> a few bytes indicating what stream # a reply (or a push, etc..) is
> associated with because its not implicit in the stream # anymore. I love
> the removal of some transport IDs from the HTTP mapping.
>
> If we take that just a touch further and include this label explicitly in
> the request as well then there is no reason to require the use of transport
> stream ids  for the labeling at all (though you could!) - and now you can
> imagine quic http being implemented on a quic transport api that looks a
> lot like existing transport apis without having to resort to inventing
> something like quic_getpeerstreamid().
>
> Relatedly the mapping defines a very short stream header to identify the
> control channel  so I don't think there is any reason to lock it onto
> stream 1 (and let the transport-id leak into the application mapping in the
> process). Its not like putting it on stream 1 guarantees it will arrive
> first anyhow.
>
> -Patrick
>
>
> On Wed, Jun 21, 2017 at 8:41 AM, Martin Thomson <martin.thomson@gmail.com>
> wrote:
>
>> I've created a pull request unidirectional streams.
>>
>> https://github.com/quicwg/base-drafts/pull/643
>>
>> I won't go into detail about this here, other than to point out that
>> there is extensive rationale for the choices I made in the PR summary,
>> a copy of which I will include for your convenience.
>>
>> ## Transport Changes
>>
>> Streams are unidirectional.  Each has three states: idle, open, and
>> closed.  I separated the transitions for sending and receiving because
>> that turned out to be easier to explain.  The reordering thing makes
>> them different in subtle ways.
>>
>> That's all.  The changes in transport are relatively small and they
>> simplify streams a lot.  That it also makes the transport more generic
>> is a nice bonus.
>>
>> ## HTTP Changes
>>
>> This is where the bulk of the changes are.
>>
>> ### Stream Correlation
>>
>> Previously request and response were implicitly correlated, as was the
>> data correlated with the request or response headers.  With this
>> change, that correlation each stream has a header that explicitly
>> correlates these.
>>
>> There are 5 types of stream: connection control, request, response,
>> data, and push.  The first two have a simple header that has a type.
>> The next two reference another stream in their header, which ties
>> request to response and data to request or response.  Push streams
>> each reference a PUSH_PROMISE using a newly minted push ID (I decided
>> that was cleaner than what I proposed at the interim).
>>
>> I chose backward references rather than forward references for two
>> reasons.  First, request and response correlation can't use forward
>> references because that would mean the client would be exerting
>> (uncoordinated) control over server streams.  Second, that gives the
>> server the most flexibility in terms of how it answers requests (see
>> #281).  The cost of using backward references is that endpoints have
>> to check that the backward references aren't bad, either referencing
>> the wrong type of stream, or with multiple references to the same
>> stream.
>>
>> The presence or absence of a message body is signaled after the
>> initial header block using a HAS_BODY frame.  This empty frame
>> indicates that another stream will include the body of the message -
>> or a promise for a body.  I would like to eliminate this stream split.
>> See #245 and #557 for more details on that, though we need to fix #176
>> first, which brings us back to QPACK/QCRAM again.
>>
>> ### Prioritization
>>
>> This changes prioritization so that it identifies requests.  I only
>> made the minimal changes here, which means that this doesn't fix #441
>> at the same time, I've left that for later (see below).
>>
>> ### Cancelling Pushes
>>
>> Because server push doesn't create streams with PUSH_PROMISE, I had to
>> create a way to cancel them between the time that the PUSH_PROMISE is
>> sent and when the push stream is created.  That's called RST_PUSH.
>>
>> ## Things That Need Improvement
>>
>> I haven't based this on #171.  I think that functionality is good, but
>> the last time I looked at the PR I didn't like some of the changes
>> Mike made there.  (That's a taste thing.)  This really assumes that we
>> accept something very much like #171.
>>
>> This really needs QPACK/QCRAM.  Right now, it is impossible to cancel
>> some types of request using RST_STREAM because not all messages will
>> have a body (#176 again).  On balance, given that bodies are what you
>> really want to kill, it's not disastrous, but it's a problem
>> nonetheless.  I think that we all agree that this is something that we
>> need to fix, but we haven't reached the point where we agree on the
>> details of the fix.
>>
>> ## Things That Want Improvement
>>
>> This also really wants headers and data on the same stream .  It
>> doesn't exactly need that, but merging the streams would make things a
>> lot easy, both conceptually and structurally.
>>
>> I chose the simplest possible encoding for all of the fields that I
>> touched.  That means that stream IDs are all 32 bits in size and the
>> HAS_BODY frame takes an entire 9 octets.  For things that are that
>> common, there are many ways in which byte efficiency could be
>> improved.
>>
>> The push ID changes might allow us to trivially fix #441.  Use of an
>> as-yet-not-created push ID as a node in the priority tree would allow
>> for prioritization to use "empty" nodes.  We'd need to explicitly
>> allow that though.
>>
>> # Fixes
>>
>> Closes #515, #240, #281, #175.
>>
>>
>