W3C home > Mailing lists > Public > whatwg@whatwg.org > October 2014

Re: [whatwg] Expose XMLHttpRequest [Fetch?] priority

From: Ilya Grigorik <igrigorik@gmail.com>
Date: Tue, 14 Oct 2014 10:02:27 -0700
Message-ID: <CAKRe7JFY9E6GBH4vLZGKF3Xvxg0ZB7knHPEv+rRfcHSBwTzzaA@mail.gmail.com>
To: Chad Austin <caustin@gmail.com>
Cc: WHATWG <whatwg@whatwg.org>
On Thu, Oct 9, 2014 at 12:12 PM, Chad Austin <caustin@gmail.com> wrote:

> Hi Ilya,
>
> This thread has been a bit confusing, partially because I did not have a
> deep understanding of HTTP 2's priority semantics and partially because I
> feel your responses have been too short and vague to truly answer the use
> case described at the top of the thread.  For example, you said "0-7
> priority is not sufficient.  Please see the previous proposal.", yet I
> don't see any discussion in the previous proposal about why 0-7 is not
> sufficient.
>
> In addition, because I'm not sure it's clear, priorities and weights are *different
> concepts*.  Weights are used to allocate resources between streams of
> identical priority.  Priorities are used to specify that one request should
> be completed before another if possible.
>

That was never the case in SPDY. In v1-v3.1 priority never guaranteed
strict delivery. Priorities were always advisory for the server, and in v4
draft we changed that to the tree model that is in HTTP/2.

Also, note that this behavior is *intentional*. When using transport
priorities, you're effectively telling the server:
- I need all of these requests, please mux all the frames such that I
receive all of the data as quickly as possible
- Also, if possible, please allocate the pipe based on specified
weights/dependencies/etc

The above does not guarantee any order. Take an extreme example: B depends
on A; A is taking 3 seconds to generate on the backend, while B is ready to
ship. The server should not starve the connection while its waiting on A,
and it should pump bytes for B until its ready to return bytes for A.

If you need *strict* response ordering, then you have to choices:
- Dispatch requests from the client based on completion of previous
request, instead of firing them off simultaneously
- Change your server to enforce your own arbitrary semantics (general
purpose servers won't do this)


> Per HTTP 2, "Streams with the same parent SHOULD be allocated resources
> proportionally based on their weight."  This is not the same thing as
> priority.  Weights may have value to others, but they don't have value to
> the use case below.
>

Yes, which is why HTTP/2 introduces dependencies and weights.

*The Use Case*
>
> Our application issues thousands (sometimes tens of thousands) of XHR HTTP
> requests to populate a WebGL-rendered 3D scene.
>
> Many of those requests are higher *priority* than others, in that, if
> possible, they should be completed *before* expending bandwidth on
> lower-priority resources.  Moreover, there is no benefit to having multiple
> responses downloading simultaneously: assuming saturated bandwidth, it is
> always better here to complete one response before moving to the next.
> (Having two half-downloaded textures or meshes is not useful.  Having one
> fully-downloaded texture or mesh and one not started yet is useful.)
>

Makes sense. To match this, create a tree that captures these relationship.


> From a software development point of view, the ideal JavaScript API would
> allow issuing *all* requests simultaneously, indicating their relative
> priority to the browser, and let the browser map the desired priority to
> the underlying protocol stack.
>

Dependencies and weights. You're using priority values as crude levels to
indicate "ordering". Instead, identify think of each "priority level" as a
level in the deps tree, where each level can have multiple requests,
resources for which can be further customized via weights.

>
> *Current Browser Behavior*
> Browsers have widespread support for two protocols (HTTP and SPDY) and are
> likely to support HTTP 2 very soon.
>

They will support HTTP/2, it's only a question of how soon. Latest FF and
Chrome are already rolling out HTTP/2 support in stable channels, and the
plan (for all browsers) is to actively deprecate SPDY.


> *So what interface should the browser expose?*
>

> Before designing a browser API, a few questions must be answered:
>
> 1) Should the priority API support legacy HTTP and SPDY?
>

- SPDY is going to be aggressively deprecated.
- Legacy HTTP has a lot of potential gotchas, I'd flag this as at risk.


> 2) Should the priority API support future protocols beyond HTTP 2?
>

- Yes, it shouldn't be tied directly to HTTP/2 semantics.


> 3) How likely is it future protocols will support non-dependency-based
> priority models.
>

- Note that you *can* implement whatever you want on the server. Client
sends its preferences, you do what you want.


> 3) How much work should be placed on application developers?  aka how
> closely should the API fit the application domain?
>

- I'm not sure what "application domain" means, since that'll be different
for every application. That said, yes, a sensible abstraction over the
low-level plumbing is good.


> IMO, the answers to the above questions are "yes", "yes", and "possible",
> and "better for the API to fit the application domain than for the API to
> reflect one possible underlying protocol so that a truer implementation in
> non-HTTP-2 protocols is possible".
>
> In that light, I think your proposal [
> http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Aug/0081.html
> ] is not satisfactory.  It requires application bookkeeping to map priority
> values to the linked list of XHRs, which:
>
> * in the case of SPDY, the browser must then convert back to 3-bit
> integers somehow
>

Not an issue we should worry about.


> * in the case of vanilla HTTP, the browser must convert the dependency
> tree back to 32-bit integers.  (or reimplement the browser's own priority
> queue as a stream dependency model.  I'd love to get a sense from browser
> authors if that's likely to happen.)
>

I think it's premature to block on this. And yes, I'm also wondering if we
should bother at all with 'plain HTTP'.


> *Why not expose priority as a signed 32-bit integer?*
>
> For HTTP, this API is trivial to implement: just plumb the priority value
> from Fetch down to the HTTP network stack.
>
> For SPDY, 32 bits would need to be mapped to 3 bits.  Something like
> Mozilla's algorithm is likely sufficient:
> http://mxr.mozilla.org/mozilla-central/source/netwerk/protocol/http/SpdyStream3.cpp#370
>
> For HTTP 2, mapping a 32-bit priority to an HTTP 2 linked list or skip
> list is straightforward and O(lg N):
> https://github.com/chadaustin/Web-Benchmarks/blob/master/http2/simpriority.py#L76
>

I disagree, as that runs precisely counter to our experience with SPDY.
You've cited Will's post that provides at least one concrete example where
this fails, and there are more:
https://insouciant.org/tech/spdy-prioritization-case-study-gmail/



> The final piece necessary is some calibration against the browser's own
> prioritization.  That is, the browser already prioritizes certain resources
> - what priority value should they be given?  Or, if you wanted an XHR to
> have lower-priority than a certain script tag, you could say something like:
>
> var xhr = new XMLHttpRequest;
> // assume large values are higher priority
> xhr.fetch.priority = document.getElementById('script').fetch.priority - 1;
>

In terms of high level requirements:
- The developer *must* be able to override browser set priorities.
- Developer set priorities must be on same playing field as browser set
priorities

As far as the API... I don't think `priority` is sufficient, for reasons
stated above.

---

This is all great feedback and discussion -- thanks! Let's continue
iterating...

ig


> On Wed, Oct 1, 2014 at 9:33 PM, Ilya Grigorik <igrigorik@gmail.com> wrote:
>
>>
>>
>> On Wed, Oct 1, 2014 at 8:39 PM, Chad Austin <caustin@gmail.com> wrote:
>>
>>> Weight is actually not what I want.  I want priority.  They're different
>>> concepts in that priority implies trumping and weight implies proportional
>>> resource allocation.
>>>
>>> That is, if I make 10 high-priority requests, 20 medium-priority
>>> requests, and 30 low-priority requests, I don't want ANY of the
>>> low-priority requests to consume any resources until either 1) all
>>> higher-priority requests have been serviced or 2) there are spare resources
>>> that cannot otherwise be used for higher-priority requests.
>>>
>>
>> And.. you've just defined a three-level deep dependency tree, with
>> weights for resources within each group.
>>
>
>
>> As you quoted, "Streams with the same parent SHOULD be allocated
>>> resources proportionally based on their weight."  Proportional allocation
>>> would be incorrect for this use case.
>>>
>>
>> Proportional within the same level of the tree, and based on assigned
>> weights within that level.
>>
>> ig
>>
>>
>>
>>>
>>> On Wed, Oct 1, 2014 at 8:19 PM, Ilya Grigorik <igrigorik@gmail.com>
>>> wrote:
>>>
>>>>
>>>> On Wed, Oct 1, 2014 at 7:59 PM, Chad Austin <caustin@gmail.com> wrote:
>>>>
>>>>> I don't see a way to set a priority value in there.  The specific
>>>>> wording is "Streams can be prioritized by marking them as dependent on the,
>>>>> completion of other streams".
>>>>>
>>>>> I see that a client can specify the weight of a stream and you can say
>>>>> that a stream depends on another stream.  Neither of those are what I
>>>>> want.  I simply want to specify priorities of a bunch of requests.  How
>>>>> would I do that in HTTP 2.0?
>>>>>
>>>>
>>>> Weight is exactly what you want:
>>>> http://tools.ietf.org/html/draft-ietf-httpbis-http2-14#section-5.3.2
>>>>
>>>> "A stream that is not dependent on any other stream is given a stream
>>>>    dependency of 0x0.  In other words, the non-existent stream 0 forms
>>>>    the root of the tree."
>>>>
>>>> All dependent streams are allocated an integer weight between 1 to
>>>>    256 (inclusive)... Streams with the same parent SHOULD be allocated
>>>> resources
>>>>    proportionally based on their weight. "
>>>>
>>>> In other words, if you don't care about dependencies, then don't assign
>>>> the parent. Doing so will make all streams children of the root of the tree
>>>> (0x0), and from there you can use weights to assign relative priority.
>>>>
>>>> Hope that makes sense.
>>>>
>>>> ig
>>>>
>>>>
>>>>
>>>>
>>>
>>>
>>> --
>>> Chad Austin
>>> http://chadaustin.me
>>>
>>
>>
>
>
> --
> Chad Austin
> http://chadaustin.me
>
Received on Tuesday, 14 October 2014 17:03:34 UTC

This archive was generated by hypermail 2.3.1 : Monday, 13 April 2015 23:09:31 UTC