Subject: Design: Adding ASSOCIATED_ONLY

From: James M Snell <[email-masked]>
Date: Wed, 19 Jun 2013 10:56:48 -0700

https://github.com/http2/http2-spec/pull/144

This was a technical change brought up and discussed as part of the
"layering taskforce" breakout but was never discussed in the larger
interim discussions.

Essentially, this PR would add an "ASSOCIATED_ONLY" flag to PRIORITY
and RST_STREAM frames that would allow terminating and reprioritizing
promised streams as a group.

Sending PRIORITY(ASSOCIATED_ONLY) would ONLY set the priority for
associated streams, not the referenced stream.

Sending RST_STREAM(ASSOCIATED_ONLY) would terminate ONLY the
associated streams, not the referenced stream.

Without this, we would have to send PRIORITY and RST_STREAM for each
individual associated stream, which is obviously quite inefficient.

---

From: Martin Thomson <[email-masked]>
Date: Wed, 19 Jun 2013 11:07:35 -0700

> individual associated stream, which is obviously quite inefficient.

What James omits is:

RST_STREAM is currently specified to terminate all associated streams
in addition to the parent stream.  This would remove this coupling,
which is considered by some to be problematic.

It's not possible to reprioritise associated streams as a group.  We
did agree that associated streams would inherit a priority that is
lower (by one) than the parent stream.  As it stands, changing all of
them requires first discovering the stream ID that will be used, then
sending individual PRIORITY frames for each.

There's not a lot of experience with this area of the specification.

---

From: Mike Belshe <[email-masked]>
Date: Wed, 19 Jun 2013 11:13:26 -0700

Is there a specific use case that needs this?

I suspect there are two camps of browsers:
   - those that disable push
   - those that don't disable push

If you disabled push, then these aren't needed.

If you didn't disable push, do you really need to be able to deal with
batch operations on associated streams?  (I know we can contrive a use-case
on the fly right now - that is always possible.  But if we don't *really*
need it, its just more stuff in the protocol I'd rather omit until we
really know that it is needed.)

Thanks,
Mike

---

From: Patrick McManus <[email-masked]>
Date: Wed, 19 Jun 2013 14:18:57 -0400

I think its worth noting that in spdy RST_STREAM was, as Martin says,
specified to terminate all associated streams in addition to the parent
stream. But afaict (willchan, correct me if I'm wrong) *nobody implementing
it did that*. which is a pretty strong signal that the mechanism should
just be removed from the spec rather than extended into priority semantics
too.

---

From: James M Snell <[email-masked]>
Date: Wed, 19 Jun 2013 11:26:24 -0700

Not very contrived use case: Switching away from one browser tab with
N-active push streams. Without this, we would need to send PRIORITY
frames for each individual pushed stream, which is bad.

At the interim, as part of the updated lifecycle discussions, we all
seemed to agree that the lifecycle of push streams was independent of
the originating stream, given that, if I close a browser tab with
N-active push streams, I would have to send a separate RST_STREAM for
every push stream in addition to the originating stream. This
eliminates that need.

You're right that this would be unnecessary if push was disabled, but
we are building push into the base protocol so we have to be able to
efficiently handle the case where push is not disabled. There's no way
around that.

While I am quite sympathetic to the "let's not add stuff we really
don't need" point of view, ASSOCIATED_ONLY makes a lot of sense in my
opinion, and would make it easier and more efficient to implement the
"independent stream lifecycle" notion.

On Wed, Jun 19, 2013 at 11:13 AM, Mike Belshe <[email-masked]> wrote:
> If you didn't disable push, do you really need to be able to deal with batch
> operations on associated streams?  (I know we can contrive a use-case on the
> fly right now - that is always possible.  But if we don't *really* need it,
> its just more stuff in the protocol I'd rather omit until we really know
> that it is needed.)
>
> Thanks,
> Mike
>
>
>
> On Wed, Jun 19, 2013 at 11:07 AM, Martin Thomson <[email-masked]>
> wrote:
>>

---

From: Jeff Pinner <[email-masked]>
Date: Wed, 19 Jun 2013 11:43:44 -0700

I'm going to put the PRIORITY discussion aside for a second and only
comment on RST_STREAMs.

I believe Patrick is correct -- I don't think anyone who implemented SPDY
implemented RST_STREAM as closing all associated streams. But IIRC that's
because that isn't how it is specified in the SPDY/3 spec. SPDY/3 Section
3.3 mentions Push and RST_STREAM but only talks about issuing a RST on the
pushed Stream-ID.

I think the requirement was added for HTTP/2 and isn't desirable. This was
the reason we considered adding the ASSOCIATED flag in the first place. We
wanted to clarify this issue and provide a mechanism while dropping the new
requirement.

On Wed, Jun 19, 2013 at 11:26 AM, James M Snell <[email-masked]> wrote:

> batch

---

From: Mike Belshe <[email-masked]>
Date: Wed, 19 Jun 2013 12:30:10 -0700

> frames for each individual pushed stream, which is bad.

The reason I don't find this interesting is because most sites will never
experience it.  You have to have multiple tabs able to share a connection
for prioritization to have any meaning.

So the use case you're referring to is:
   * user has multiple tabs open to the same site
   * the site does significant amount of background traffic which create
congestion for the foreground tab.

I think this is contrived.

> every push stream in addition to the originating stream. This

If you knew today that 99% of the time the number of push streams you'd
have to cancel was 2, you wouldn't care.
On the other hand, if the typical number of push streams was 100000, you'd
care.

My gut tells me it will be closer to 2 to 100000, but you may disagree.
 Either way, its not a problem we have, and the "inefficiency" is not that
inefficient!

You can pack about 200 stream resets into a single TCP/ethernet packet
so.... isn't that good enough?

I think multiple RST frames is just fine.

> "independent stream lifecycle" notion.

Fair enough. Every detail we add is complexity which makes deployment and
adoption and compatibility more difficult.  If I had to prioritize over
deployability for the core features (multiplexing & the parts that have
been proved to work already) vs scalability for the
yet-never-used-in-production features, I choose the former.

Best,
Mike

batch
> On Wed, Jun 19, 2013 at 11:07 AM, Martin Thomson <[email-masked]
>
> wrote:
>>

---

From: William Chan (陈智昌) <[email-masked]>
Date: Wed, 19 Jun 2013 12:41:56 -0700

It's vague in the SPDY 3 spec but is definitely there, just not in the
RST_STREAM section. See
http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-3.3.2-Client-implementation
:

"To cancel all server push streams related to a request, the client may
issue a stream error (Section 2.4.2) with error code CANCEL on the
associated-stream-id. By cancelling that stream, the server MUST
immediately stop sending frames for any streams with in-association-to for
the original stream."

Patrick's right and no implementation of server push has read that section.
I raised this point at least twice at the interim meeting. Roberto's
counterpoint (from the meeting) is that adding a flag for this makes it
explicit, so it won't be as easily forgotten.

I'm personally lukewarm on this and would rather be explicit and send all
the RST_STREAMs. But I don't have a strong opinion here.

---

From: Mike Belshe <[email-masked]>
Date: Wed, 19 Jun 2013 12:45:18 -0700

> Patrick's right and no implementation of server push has read that
> section. I raised this point at least twice at the interim meeting.
> Roberto's counterpoint (from the meeting) is that adding a flag for this
> makes it explicit, so it won't be as easily forgotten.
>
>> I think the requirement was added for HTTP/2 and isn't desirable. This
>> was the reason we considered adding the ASSOCIATED flag in the first place.
>> We wanted to clarify this issue and provide a mechanism while dropping the
>> new requirement.
>>
>>
>>
>>
>>
>>
>> On Wed, Jun 19, 2013 at 11:26 AM, James M Snell <[email-masked]>wrote:
>>

---

From: Mike Belshe <[email-masked]>
Date: Wed, 19 Jun 2013 12:46:59 -0700

> makes it explicit, so it won't be as easily forgotten.

And so it seems that even people that wrote that spec have forgotten! :-)

But, given what we know now, I still think that sending RST_STREAM for each
stream is sufficient and simplest.

Mike

---

From: James M Snell <[email-masked]>
Date: Wed, 19 Jun 2013 12:50:01 -0700

In the short term (for the implementer's draft) I'm perfectly fine
deferring this particular item, I just wanted to make sure the issue
was documented and available for discussion. The pull request has been
submitted and can easily sit there for a bit while we figure the other
items out :-) ...

On Wed, Jun 19, 2013 at 12:46 PM, Mike Belshe <[email-masked]> wrote:
>
>
>
> On Wed, Jun 19, 2013 at 12:41 PM, William Chan (陈智昌) <[email-masked]>
> wrote:
>>
>> http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-3.3.2-Client-implementation:
>>
>> associated-stream-id. By cancelling that stream, the server MUST immediately
>> stop sending frames for any streams with in-association-to for the original
>> stream."
>>

---

From: Jeff Pinner <[email-masked]>
Date: Wed, 19 Jun 2013 12:55:55 -0700

Thanks for finding that in the SPDY spec! You're right, I totally missed
reading that line.

I'm happy to wait on adding the flag but I would like to drop the
requirement that a server close all associated resources.

In the SPDY spec the life-cycles of the pushed resources were much more
tightly coupled with the original stream because there was no PUSH_PROMISE
so all streams had to be created while the original stream was still open.

With PUSH_PROMISE, the opening of the new streams can occur well after the
original stream closes so this behavior no longer makes and sense.

On Wed, Jun 19, 2013 at 12:50 PM, James M Snell <[email-masked]> wrote:

> immediately
> >>>> > operations on associated streams?  (I know we can contrive a
> use-case
> >>>> > on the

---

From: Patrick McManus <[email-masked]>
Date: Wed, 19 Jun 2013 16:00:57 -0400

> requirement that a server close all associated resources.

+1.

---

From: Martin Thomson <[email-masked]>
Date: Wed, 19 Jun 2013 13:07:57 -0700

On 19 June 2013 11:43, Jeff Pinner <[email-masked]> wrote:
> I think the requirement was added for HTTP/2 and isn't desirable.

It's inherited from SPDY/3.  At least the version that we got.

---

From: Mike Belshe <[email-masked]>
Date: Wed, 19 Jun 2013 13:20:01 -0700

sgtm, +1 too.

---

From: Amos Jeffries <[email-masked]>
Date: Fri, 21 Jun 2013 04:20:29 +1200

On 20/06/2013 7:30 a.m., Mike Belshe wrote:
>
>
>
> On Wed, Jun 19, 2013 at 11:26 AM, James M Snell <[email-masked] 
> <mailto:[email-masked]>> wrote:
>
> The reason I don't find this interesting is because most sites will 
> never experience it.  You have to have multiple tabs able to share a 
> connection for prioritization to have any meaning.
>
>    * the site does significant amount of background traffic which 
> create congestion for the foreground tab.
>

Possibly. I can also think of a very likely scenario which this will 
cause problems for.

1) Middleware which is caching the pushed resources will not be happy to 
have them terminated incompletely received even if the user has closed 
the main non-cacheable stream.

2) Middleware serving the pushed objects up to multiple clients in 
parallel will be quite put out to have the streams discontinued 
underneath it simply because they were associated as secondary resources 
with _one_ particular client who went away.

Per-stream reset frames can be recieved and the reset easily dropped if 
the stream is shared by multiple active clients or otherwise desired to 
be kept going. Retaining additional state on every stream to record the 
associations it has to all other streams is an unwelcome complexity.

> If you knew today that 99% of the time the number of push streams 
> you'd have to cancel was 2, you wouldn't care.
> On the other hand, if the typical number of push streams was 100000, 
> you'd care.
>
> My gut tells me it will be closer to 2 to 100000, but you may 
> disagree.  Either way, its not a problem we have, and the 
> "inefficiency" is not that inefficient!
>

Same here.

Amos

---

From: James M Snell <[email-masked]>
Date: Thu, 20 Jun 2013 09:53:03 -0700

On Thu, Jun 20, 2013 at 9:20 AM, Amos Jeffries <[email-masked]> wrote:
>[snip]
>
> Possibly. I can also think of a very likely scenario which this will cause
> problems for.
>
> have them terminated incompletely received even if the user has closed the
> main non-cacheable stream.
>
> 2) Middleware serving the pushed objects up to multiple clients in parallel
> will be quite put out to have the streams discontinued underneath it simply
> because they were associated as secondary resources with _one_ particular
> client who went away.
> [snip]

Not following the thought here. There are several important things to remember:

1. Pushed-Streams are not shared, and
2. Pushed-Streams are always hop-by-hop

This was a point that was raised at the interim and discussed at length.

For example, suppose a client A sends a request through caching proxy
B to origin server C.

C receives the request and sends PUSH_PROMISE frames to B as part of
its response. B can accept those pushed streams from C but it is not
required to send PUSH_PROMISE frames to A. It can choose, for
instance, to just use the pushed streams to populate it's local cache
and have A send GET requests for each of those cached resources. There
might be a delay in B responding to those GET's while it receives the
data from C, but that's not important here. If B does decide to send
PUSH_PROMISE frames to A, those pushed streams are completely
independent of the streams C is pushing to B, even if they are for the
same resources. A can send a RST_STREAM to B terminating a pushed
stream, but that has absolutely no effect on the stream being pushed
from C to B, even if it is for the same resource. And A sending a
RST_STREAM for a pushed stream has absolutely no effect on any other
stream B might be pushing to any other client. So, sending a
RST_STREAM(ASSOCIATED_ONLY) has ZERO impact on what middleware might
do with multiple clients. It only terminates the pushed streams being
exchanged between two points.

- James

---

From: Amos Jeffries <[email-masked]>
Date: Fri, 21 Jun 2013 13:19:45 +1200

On 21/06/2013 4:53 a.m., James M Snell wrote:
> On Thu, Jun 20, 2013 at 9:20 AM, Amos Jeffries wrote:
>> [snip]
>>

By shared I mean multiple clients may receive the contents. As in shared 
cache vs. private cache.

If the meaning of "shared" for pushed streams still menas the same thing 
it used to they are to be potentialy large streams of non-cacheable data 
containing objects which in a client-fetch stream would be perfectly 
cacheable?
  I can tell you in a nutshell what middleware will do with that type of 
traffic: RST_STREAM.

> instance, to just use the pushed streams to populate it's local cache

But caches are for shared resources. Caches can also be read from while 
the object is still being received from server. Squid collapsed 
forwarding was the feature I had in mind when I wrote the above case 
description.
  Refer (1) "Pushed-Streams are not shared". I smell a definition problem.

> exchanged between two points.

Go it. Thank you.

Amos