RE: draft-lubashev-quic-partial-reliability

"Lubashev, Igor" <ilubashe@akamai.com> Wed, 20 December 2017 01:21 UTC

Return-Path: <ilubashe@akamai.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 0A88E128961 for <quic@ietfa.amsl.com>; Tue, 19 Dec 2017 17:21:41 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: 3
X-Spam-Level: ***
X-Spam-Status: No, score=3 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, GB_SUMOF=5, HTML_MESSAGE=0.001, SPF_PASS=-0.001] autolearn=no autolearn_force=no
Authentication-Results: ietfa.amsl.com (amavisd-new); dkim=pass (2048-bit key) header.d=akamai.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 4OZcFMMH4fv8 for <quic@ietfa.amsl.com>; Tue, 19 Dec 2017 17:21:38 -0800 (PST)
Received: from mx0b-00190b01.pphosted.com (mx0b-00190b01.pphosted.com [IPv6:2620:100:9005:57f::1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id C252212AF77 for <quic@ietf.org>; Tue, 19 Dec 2017 17:21:37 -0800 (PST)
Received: from pps.filterd (m0122331.ppops.net [127.0.0.1]) by mx0b-00190b01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id vBK1Gpnq022347; Wed, 20 Dec 2017 01:21:34 GMT
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=akamai.com; h=from : to : cc : subject : date : message-id : references : in-reply-to : content-type : mime-version; s=jan2016.eng; bh=UcezZAw/sjXGGJS0ViFdnjVmwsXSiEFNVcKCFMJoqc4=; b=HY478c9z3pc/Rjdf0IAyJQzMMRwSoFx27Bu9hXwMd9EgF6RarkldouzFnh1JuzhCY/4m M7CznbBi38UWzt6E76G0CxbqlRiGf+Kmh14Xs5IAKj1GhQ8remtT2OxdYJMYT2m/a8uD AXmjVGwByDxbtWUjXr1KgaEVZpFCBU3uWENfv3dzINUiuphXNjINwpuW/1PzdCYgE/+V 9ONgCXIRIcd9s6T5HQQrFGBWPzqLwleholW3nUdL4VfG3QBoDCC1cdBZT4FUr6xqp29M V3bWcMqyomhpASpvmiqiN6UpNBSTgzrFOF/u1cdY7JOTdFT04DAElxUyzWv+/1bIgMVZ kA==
Received: from prod-mail-ppoint1 (prod-mail-ppoint1.akamai.com [184.51.33.18]) by mx0b-00190b01.pphosted.com with ESMTP id 2evs7v30xr-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 20 Dec 2017 01:21:33 +0000
Received: from pps.filterd (prod-mail-ppoint1.akamai.com [127.0.0.1]) by prod-mail-ppoint1.akamai.com (8.16.0.21/8.16.0.21) with SMTP id vBK1LI5s018163; Tue, 19 Dec 2017 20:21:33 -0500
Received: from email.msg.corp.akamai.com ([172.27.123.31]) by prod-mail-ppoint1.akamai.com with ESMTP id 2evypxxhj2-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Tue, 19 Dec 2017 20:21:32 -0500
Received: from USMA1EX-DAG1MB5.msg.corp.akamai.com (172.27.123.105) by usma1ex-dag1mb4.msg.corp.akamai.com (172.27.123.104) with Microsoft SMTP Server (TLS) id 15.0.1263.5; Tue, 19 Dec 2017 20:21:32 -0500
Received: from USMA1EX-DAG1MB5.msg.corp.akamai.com ([172.27.123.105]) by usma1ex-dag1mb5.msg.corp.akamai.com ([172.27.123.105]) with mapi id 15.00.1263.000; Tue, 19 Dec 2017 20:21:32 -0500
From: "Lubashev, Igor" <ilubashe@akamai.com>
To: Ian Swett <ianswett@google.com>, Lucas Pardue <Lucas.Pardue@bbc.co.uk>
CC: "quic@ietf.org" <quic@ietf.org>
Subject: RE: draft-lubashev-quic-partial-reliability
Thread-Topic: draft-lubashev-quic-partial-reliability
Thread-Index: AdN4eekQTxka84DaTqiLnO0Um5bpqQAMOJHZAAgTzPAAAesSEAAXdj+AAANLonA=
Date: Wed, 20 Dec 2017 01:21:31 +0000
Message-ID: <672822da83a74bd2b69634c881283331@usma1ex-dag1mb5.msg.corp.akamai.com>
References: <c1f5a6d8d21f423a93003f7b69dae882@usma1ex-dag1mb5.msg.corp.akamai.com> <7CF7F94CB496BF4FAB1676F375F9666A3BAB1A4A@bgb01xud1012> <85d7dc28d3bb40a38873f4d32a16fd71@usma1ex-dag1mb5.msg.corp.akamai.com> <7CF7F94CB496BF4FAB1676F375F9666A3BAB1B01@bgb01xud1012> <CAKcm_gMQ5H9Q9N4KNAbYDM23S9o=964t-o17jV8EdgW0nJ8jpA@mail.gmail.com>
In-Reply-To: <CAKcm_gMQ5H9Q9N4KNAbYDM23S9o=964t-o17jV8EdgW0nJ8jpA@mail.gmail.com>
Accept-Language: en-US
Content-Language: en-US
X-MS-Has-Attach:
X-MS-TNEF-Correlator:
x-ms-exchange-transport-fromentityheader: Hosted
x-originating-ip: [172.19.35.55]
Content-Type: multipart/alternative; boundary="_000_672822da83a74bd2b69634c881283331usma1exdag1mb5msgcorpak_"
MIME-Version: 1.0
X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-12-19_12:, , signatures=0
X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1711220000 definitions=main-1712200017
X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-12-19_12:, , signatures=0
X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1011 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1711220000 definitions=main-1712200016
Archived-At: <https://mailarchive.ietf.org/arch/msg/quic/h5AYVzGUePoeGLrYmEPxVvC0O80>
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, 20 Dec 2017 01:21:41 -0000

  *   This proposal requires out of order delivery be supported by QUIC streams

It does not.  All delivery, whether of data or gaps-in-data, is strictly in-order.  I can see a receiver API look something like:

class Stream {
      …
size_t receive(void *buf, size_t buf_size) throw QuickException;
size_t receive(size_t &expired_bytes, void *buf, size_t buf_size) throw QuickException;
…
};

The version of receive with expired_bytes will return how many bytes have expired followed by the data received in buf.  Clients that do not expect missing messages will just call the version of receive() w/o expired_bytes and will get an exception if any data is expired.   (APIs that are event callback based will just declare a new stream event type “StreamExpiredData”.)




  *   Also, the receiver needs to be able to determine what the message boundaries are if there are gaps in the stream.

It will not.  Since sender keeps track of where message boundaries are and controls minimum retransmittable offset, the receiver is guaranteed that any data after a gap corresponds to the start of a new message.
(MAY in Section 6 “Receiver Interface and Behavior” is not a MUST to allow for an optimization of keeping data that has arrived after MIN_STREAM_DATA due to reordering, if the application has not yet been notified about the data gap.)



  *   Can a STREAM frame contain the end of one message and the beginning of another in a single frame?

Yes, it can, since nothing is changed in the behavior of STREAM frames.  The assumption is that the application can figure out itself how long each fully-delivered message is.



  *   In section 4, you discuss the separation of Unsent bytes from Sent data, but I'm still confused on the motivation there, since separating them seems to make flow control more complex.

I guess I failed to be clear here, as Unsent Bytes was designed to NOT complicate flow control accounting (in fact, it has no effect on flow control accounting at all).

Imagine an HD video conferencing app has sent 10MB of data to a stream (maybe in multiple messages).  Now, say, it was only able to transmit 1MB out of those 10MB when a new key frame is received.

If it has to use normal stream and connection flow control credits to advance this stream by the additional 9MB, it may might not be able to do so w/o violating MAX_STREAM_DATA or MAX_DATA.  The sender would need to “skip” those 9MB over several RTTs, if the receiver is only willing to commit, say, 2MB of buffers to this stream/connection (it is not aware that the sender has 9MB of octets to skip).  Moreover, this would be blocking all other streams from making progress by closing down the connection window.

Unsent Bytes was designed to solve this program, as it *does not count toward stream or connection flow control* (so it does not violate them and does not reduce connection window), but it is a hint to open the stream and connection windows to accommodate skipping byte octets (since they do not require any buffer space).  The stream would be able to resume in at most 1 rtt, and it would not block other streams meanwhile.

However, I now see a problem – while opening the stream window by 9MB+ is ok (since any octets at offset lower than Min Stream Offset are to be discarded), there is no requirement that the extra connection window is used for that stream only.  A malicious client can render connection window meaningless by sending MIN_STREAM_DATA with a huge Unsent Bytes and then never sending on that stream again.  Oops!  I will fix this!




  *   I believe there is another challenge, which is indicating to the receiver when they should stop expecting to receive data.  Conveniently, this is exactly what MIN_STREAM_DATA does, but I think it's worth noting in the introduction.

I meant exactly this by “notifying the transport and the peer when data previously enqueued for transmission no longer needs to be transmitted“ (that’s what “and the peer” was for) but said it very awkwardly.  Will fix by focusing only on the peer (interaction between the app and local transport does not concern wire protocol).



  *   Igor



From: Ian Swett [mailto:ianswett@google.com]
Sent: Tuesday, December 19, 2017 3:20 PM
To: Lucas Pardue <Lucas.Pardue@bbc.co.uk>
Cc: Lubashev, Igor <ilubashe@akamai.com>; quic@ietf.org
Subject: Re: draft-lubashev-quic-partial-reliability

Thanks for writing this up Igor, it's an interesting idea, and I like how it allows any stream besides 0 to be partially reliable.  I'm a bit concerned(or possibly confused) about the effort to change how flow control works, see comments below.  I believe this proposal implicitly assumes that it's easier and more efficient to use streams for ordering and make the application deal with interpreting out of order data than the current model of making applications reorder a large number of small streams?  That may or may not be true in a given application, but I can imagine cases when it is true.

At the beginning you say:
"The key to partial reliablity is notifying the transport and the peer
 when data previously enqueued for transmission no longer needs to be
 transmitted."

I believe there is another challenge, which is indicating to the receiver when they should stop expecting to receive data.  Conveniently, this is exactly what MIN_STREAM_DATA does, but I think it's worth noting in the introduction.

In terms of the downsides of using streams as is, you say:
"Hence, a message-per-stream
 approach requires each message to contain an extra header portion to
 associate the message with a logical application stream.  In case of
 short messages, this approach introduces a significant overhead due
 to STREAM frames and message headers.  It also places the burden on
 the application to reorder data arriving on multiple QUIC streams."

In practice, I believe a lot of applications using partial reliability today have such headers, since they're commonly designed with UDP in mind.

In section 4, you discuss the separation of Unsent bytes from Sent data, but I'm still confused on the motivation there, since separating them seems to make flow control more complex.  Currently the connection flow control used is the sum of the largest byte offsets of all streams on a connection, and now that would be dramatically different, correct?  Can you add more background on why these are separated and how they are applied to various limits?  If I read correctly, MIN_STREAM_DATA frame is effectively able to increase a peer's flow control limits by declaring previously used credits up for grabs?

This proposal requires out of order delivery be supported by QUIC streams, which is worth explicitly noting, maybe in section 5, since it's currently not required.  Also, the receiver needs to be able to determine what the message boundaries are if there are gaps in the stream.  How easy that is would depend upon what is being sent on the stream.  It might deserve some discussion about how one might do this and what restrictions should be imposed on how the sender bundles data, if that ends up being necessary?  ie: Can a STREAM frame contain the end of one message and the beginning of another in a single frame?


On Tue, Dec 19, 2017 at 10:46 AM, Lucas Pardue <Lucas.Pardue@bbc.co.uk<mailto:Lucas.Pardue@bbc.co.uk>> wrote:
Igor wrote:

The bookkeeping is simple enough and the runtime resources required is just one uint64_t per steam, so I do not see a problem for constrained devices.
Ultimately, partial reliability is a feature that an application either needs or does not. If an application needs it, it most likely requires it. In any case, an application can do its own negotiation if it desires to do so. For example, it can use stream 1 for that.
Now I understand this is connection-wide, I generally agree. However, for something like conventional HTTP/QUIC, the reliable guarantee assurances are required by the mapping. If partial reliability is default enabled transport feature, applications like HTTP/QUIC must restrict or disable it – I’m not sure if the editors/drafts have broached that subject yet (I’ve certainly had some feedback on my draft about such restrictions).

I did not fully understand the last question. Are you asking whether it is possible for the connection to negotiate just a single stream (in each direction) that would be partially reliable and, therefore, not send a stream id with MIN_STREAM_DATA? I could actually think of a version of MIN_STREAM_DATA that has an implied stream id -- the stream id of a prior/following STREAM frame in the same packet. That would be an optimization that I am happy to add, if there is enough support for it.

Apologies, I hadn’t drunk any coffee when first replying and got myself confused. All QUIC frames of this nature should normally include a Stream ID, which identifies the stream it is sent on. I think your suggestion relies on serial ordering/processing (even inside a packet), which might be a hard sell.

Separately, in terms of asymmetry, I was thinking that you might always want (as a policy) clients to transmit with full reliability but provide data to them with partial reliability. I think this is again an application mapping concern.

Regards,
Lucas