WICG/priority-hints

Support SW install usage case via new property on Request interface

jeffposnick opened this issue · 14 comments

This falls into the buckets of things that are probably going to be covered by virtue of the existing placeholders for the Fetch API usage examples, but I wanted to make sure that whatever solutions are proposed end up taking a specific use case into account: control over priority for requests used to populate caches inside of a service worker's install handler.

The most common way of doing that is not to use the Fetch API directly, but instead to use the Cache Storage API's add() or addAll() methods. Those methods can take in Request objects as parameters, in the same way that the Fetch API can.

So it would be preferable if the mechanism for indicating priority where associated with the Request interface, and another something directly tied to the Fetch API's fetch() method. I'm not well-versed in the language of specifications, but it seems like extending https://fetch.spec.whatwg.org/#request to include:

interface Request {
  ... existing fields ...
  readonly attribute RequestPriority priority;
}

dictionary RequestInit {
  ... existing fields ...
  RequestPriority priority;
}

enum RequestPriority { "placeholder1", "placeholder2", ... etc. };

would be sufficient. The placeholder values would correspond to whatever strings are agreed upon in #7

I'm happy to file this against the Fetch API spec eventually, but it seems like that might be premature at this point.

I agree with that approach, currently something very similar to what you suggest is what I proposed in Chromium's design doc, if that interests you at all.

@jeffposnick If you get a chance, would you mind taking a look at the draft section proposed in the design doc? We've published the intent to implement PR today and hope to follow up with issues/PRs against both the Fetch spec and HTML standard as time goes on. Just want to sanity check we're addressing the use-case you had sufficiently :)

Circling back on this issue to discuss a little more about potentially exposing the importance attribute on the Request interface. Right now in Chromium's experimental implementation:

  • We don't actually expose the attribute on the Request attribute
  • But it exists as one of the Requests internal slots (more or less), so we actually get to cover your use-case (cache API, not just fetch()) but without exposing as a readonly attribute.

@yutakahirano was initially a proponent of not exposing importance as a readonly attribute on the interface, I think largely to lower the implementation barrier for other vendors (I could be wrong), but given:

  • The recent discussion we've had on the WebPerf WG call alluding to the intrigue of eventually standardizing resource load priorities
  • Use-cases that @LPardue might be able to contribute discussion to (context: this tweet)

...are there some good use-cases for exposing this property that ServiceWorker app code might be able to take advantage of? Happy to continue this discussion elsewhere, perhaps in another issue, but it seems worth bringing up in general.

Apologies, but I'm not very familiar with some of the terminology common in specifications or the internal implementation of objects within browsers. I am not 100% clear on what

But it exists as one of the Requests internal slots (more or less), so we actually get to cover your use-case (cache API, not just fetch()) but without exposing as a readonly attribute.

means.

I think you're saying that importance might be allowed to be set to a specific value when a Request is constructed, but post-construction, importance isn't going to be exposed as a publicly-readable value.

If my interpretation is correct, then sure, I think that would be sufficient to take care of the use case I had in mind—I care about being able to set it at creation, rather than reading the value from an arbitrary Request object after-the-fact.

Sorry for the lack of clarity! Yeah, basically importance always exists and can be used and set by internal stuff at any time, but the only time application code (namely JS) can get at it is during construction via RequestInit. With this, I wonder if there is a ServiceWorker (or perhaps other) use-case for exposing it as a read-only property on the Request object, to get at it after-the-fact as you mention. But I'll let Lucas and others decide whether we should break that out into a separate issue.

Thanks!

I could imagine a service worker's fetch handler that... I dunno, normally didn't cache image requests, but if an image request came in with high importance, added that to the cache. But that's a stretch, and is usually the sort of determination that is made via URL paths or some other heuristic.

Being able to inspect that field post-construction is not high on the list of things I would consider important.

I think it should probably be exposed on Request for a few reasons:

  1. Consistency. We don't really have many (any?) other properties you can set via RequestInit that are not also exposed on Request.
  2. This is partly because its a common pattern to use a Request as a RequestInit. Like new Request(newURL, oldRequest). If we don't expose the value on Request, then it will be dropped for this usage.
  3. The service worker should be able to do more complex fetching in response to a FetchEvent. This may mean creating one or more Requests. Ideally it should be able to propagate the original importance forward in these cases.

I am also in favor of exposing Request.importance. I think consistency is a big thing and agree with that point; regarding (2), that's interesting, I knew you could use a Request as a RequestInfo, but did not realize people were doing it for RequestInit as well. This gives us more testing opportunity and a strong argument in favor of exposing.

So my thinking on this topic probably just echoes what has been expressed already here or on related issues.

Since priority hints, namely the importance property, is an emerging capability I would assume there is room for testing and tuning. In the absence of this property being available, the Service Worker has to act as a blanket overwrite. An input signal could help it be smarter - being the gateway for all resource loading could give it an overview position not available elsewhere. I could see some priority hints equivalent to Workbox being useful to test out prioritisation strategies.

To capture my thoughts from Twitter:

  1. High importance requests could be a good candidate for caching (as @jeffposnick mentions above)
    a) conversely, low importance requests may be good candidates for deletion on constrained devices
  2. I have a Service Worker for a particular use case that in response to certain URLs goes off and makes a number of HEAD requests. This uses a Promise.all() approach. Once all requests resolve I do some batch processing before returning. This creates some slight blocking in the Service Worker but is required for the use case. With client-indicated importance I may chose to do something that makes this activity longer or shorter i.e create a smaller array etc.
    b) If a Service Worker could create a Web Worker, I could delegate lower importance activities to such a worker, and only block Service Worker on more critical things.

Thanks @LPardue for weighing in and sharing your use-case, quite interesting! I think these are really good points you bring up, and definitely strong arguments in favor of exposing importance on Request objects. I'll get moving on changing Chromium's implementation such that importance is exposed properly.

From the W3C TAG review request, there is a concern that the current explainer doesn't demonstrate how to use importance with fetch, however the latest spec draft does.

It'd be great for to see these expanded examples included in the Explainer as well.

On a related point, will the Request handed to the Service Worker's onfetch event reflect the priority that request was generated with from the document?

It'd be great for to see these expanded examples included in the Explainer as well.

This is a great point, and reminds me of something I've been meaning to ask regarding the Explainer doc. As the spec matures, and becomes more fleshed out and well-shaped, are we to maintain the Explainer doc at equal pace, or does it eventually get "deprecated" in favor of the spec? Apologies if this is a rookie question...it seems that they are essentially overlapping as the spec matures though, no?

will the Request handed to the Service Worker's onfetch event reflect the priority that request was generated with from the document?

If we s/priority/importance, then the answer is yes. In Chrome's flagged implementation of this right now, importance is never exposed on Request objects, but when it is, it should be exposed to Request objects handed to onfetch.

@wanderview The spec has it added as a property of Request and I'm working on plumbing it through Chrome, but to set expectations:

I think it should probably be exposed on Request for a few reasons:

  1. Consistency. We don't really have many (any?) other properties you can set via RequestInit that are not also exposed on Request.
  2. This is partly because its a common pattern to use a Request as a RequestInit. Like new Request(newURL, oldRequest). If we don't expose the value on Request, then it will be dropped for this usage.

By the time a request makes it to the service worker (in Chrome), it has already been prioritized and there is an internal "priority" that is carried through. I believe it is copied through when you clone a request but I should verify.

  1. The service worker should be able to do more complex fetching in response to a FetchEvent. This may mean creating one or more Requests. Ideally it should be able to propagate the original importance forward in these cases.

There's a bit of risk here that developers should probably be aware of. Multiple requests for the same resource get de-duplicated so the worker will only see the details of the first request but if a later request is for a higher priority, it will be reprioritized internally but there are no callbacks to the worker to let it know of priority changes or the context of multiple requests de-duped to the same fetch.

There's a risk of thinking a request is low priority in a worker since that's all it would see on the hint even if a high-priority request was also linked to it. It's a bit of an edge case but something to be careful of before getting too fancy (like lazy-loading it or requesting a low-resolution version or something).