Manual priority control of resource fetching


#22

I’m not so sure about this, as it kinda ties you to what a particular browser does today. Eg, if a browser later decides to make images higher priority, your +1 may make their priority too high.

I’m coming round to something like:

<img fetch-class="shop-item first-ten" higher-priority-than="fetch-classes" lower-priority-than="fetch-classes">

(I haven’t given thought to the naming)

We’d need dynamic priorities to deal with the “B depends on A and browser never discovers A” case. As in, if A is never discovered, it has no priority impact, but if A later turns up while B is in-progress, priority can change to A.

Do we need a way to say “A should have a slightly higher priority than B” vs “Any bandwidth spent on B at the expense of A is bad”? Or is the latter enough?


#23

I think there also needs to be a way to limit priorities per domain. I could see situations were third party widgets would mark all their downloads as highest priority, an embedding site considering its own priorities would get overridden or end up in a situation were everything is marked as priority 1 in order to combat it.


#24

A way to express priority on an iframe (and therefore all fetches it performs) would do this.

I don’t think we can add anything to police 3rd party scripts running on the same origin. Any switches we add could just be switched back.

If we decide that the priority of an iframe affects the fetches it makes, would we say the same about dedicated workers & css?


#25

Fair.

The would require us to define default “fetch-class” values for current resource types, so that you e.g. use <img higher-priority-than="important-resources" lower-priority-than="render-critical-resources"> to say “this image should be fetched in high priority, after any critical resources”.


#26

I guess that’s possible, but I’m struggling to figure out when you’d need that.

I suppose you might want to say “this image is higher priority than font downloads”, and I’m not sure how you’d express that.

You’d either need a way to tag something with a fetch class within CSS (but that doesn’t help with 3rd party CSS), or as you say have some reserved classes (like all of preload’s “as” values, which includes “font”). I suppose user defined values could require a hyphen, like web components :smile:.


#27

You’d mostly need that when you’d want to impact the download priority of a single resource without adding “fetch-class” attributes on all other resources. If our solution would require people to perform a lot of manual work in order to cover the basic cases (which are most of the cases I heard from people), they won’t. Providing default fetch-classes would enable people to express what they need without marking-up all the resources (including the ones loaded by scripts which are out of their control and, as you mentioned, CSS based resources).


#28

Hmm yeah. Summarising the different ways a fetch can be identified in terms of relative prioritisation:

URL

  • :white_check_mark: You can target it no matter how the fetch was triggered.
  • :negative_squared_cross_mark: Could get verbose & violate DRY if you’re trying to prioritise something higher than “all images”.
  • :negative_squared_cross_mark: In some cases you don’t know the URL ahead of time, eg Google fonts.

CSS selectors

  • :white_check_mark: Allows you to target all elements of a type, whether they’ve been specifically tagged or not.
  • :white_check_mark: Nth-child lets you do some positional stuff
  • :negative_squared_cross_mark: CSS background images & fonts can’t be selected.
  • :negative_squared_cross_mark: fetch() can’t be selected.

Destination

  • :white_check_mark: You can target types of requests, eg “font”.
  • :negative_squared_cross_mark: Not granular enough. eg “image” means both img and CSS backgrounds.

“Fetch class”

  • :white_check_mark: Allow you to tag elements and also programatic fetches.
  • :negative_squared_cross_mark: Doesn’t work if the fetch is initiated by a third party, and they haven’t given it a fetch class, eg font CSS.
  • :negative_squared_cross_mark: No way to assign a fetch class for CSS fetches. We’d have to invent new syntax.

Maybe we need some combination of the above, or a better idea. It may also be a sign that we’re trying to go high-level too early.


#29

It sounds like there’s strong support for the need for this capability.

Is it declarative markup, an argument to fetch(), or a serviceworker-like JS that manages network connections? Or all of the above?

My take is that there’s value in exploring a declarative markup solution to this (an attribute-based approach that indicates a resource is higher or lower priority to others) in addition to a lower-level JS-based one (Jake’s FetchController suggestion lower down).

How do these priorities map to “default” (and unspecified :)) browser priorities?

I’m a fan of expressing to a UA that some resources are more or less important than others (rather than relying on integers) and allowing the browser to then map out what this should mean when when factoring in the priorities of other mechanisms like H2 and QUIC.

Defining full dependency trees in markup can become complex fairly quickly, but maybe we can define some form of dependency, which can help the browser define smarter H2 dependency trees.

I concur. If we can devise a design for expressing dependencies without it requiring the full-blown tree to be specified in markup, that sounds more usable. The dependency resolution note you raised (“B depends on A and browser never discovers A”) is fair and definitely something we’ll want to work out how to handle.

If our solution would require people to perform a lot of manual work in order to cover the basic cases (which are most of the cases I heard from people), they won’t.

Excessive manual annotation would limit the usability of this feature (or heavily defer it to build tools to help provide better abstraction) so trying to find a balance in the design that avoids the need for this would be desirable.

You’d mostly need that when you’d want to impact the download priority of a single resource without adding “fetch-class” attributes on all other resources.

Yoav: could you expand on this idea of default fetch-classes with some examples of what this would mean for resources you’re not annotating with a “fetch-class” directly?


#30

Glad to see declarative ‘lazy-loading’ getting picked up again.

One thing I haven’t seen mentioned above is on-demand loading - i.e. don’t load the image until the user has scrolled a reasonable distance down the page. This is the behaviour that some js lazy-loading solutions choose. Depending on network conditions, it can be an antipattern, but can also be a huge bandwidth saver, so some kind of API to achieve this would be useful. It’d more be a case of having a declarative way in to something like IntersectionObserver, so perhaps not all that closely related to resource prioritising, but a unified API would be nice.

I’d prefer lower level (and likely verbose) APIs at first. Not because I relish defining dependency trees etc, but more because this debate reminds me a little of responsive images. From the complex yet flexible Picture element we learned a lot, and it led the way to the simple and good-enough-in-most-cases srcset + sizes API. There are so many pitfalls around resource loading it feels like we need to use something low level for a while in order to learn what we want.


#31

I’ll echo a lot of the same points.

I like Jake’s proposal for exploring notion of groups. Conceptually, that’s how we think about different resources types under the hood in browsers today, and it translates well to “user-space” where you have different types of content that shares similar properties. The trick here is to define a mapping between browser + user-space groups, but perhaps that doesn’t have to be that complicated either… I think we could even think of Fetch#destination as groups. Once we have the Fetch concepts defined, mapping them to declarative markup should be relative simple.

I think we should work under assumption that “browser will do the right thing by default ™” and priority group controls are exceptional overrides that should be used sparingly. Similarly, specifying a group/priority is not a guarantee that the UA must respect it… UA has the situational knowledge of the environment and is free to optimize based on provided defaults and priority hints.


#32

I’ve set up a WICG GH repo for this and wrote down the use cases mentioned here. Use case PRs welcome! :slight_smile: Let’s discuss the suggested solutions on issues there.


#33

Probably both declarative and a fetch() argument.

I believe exposing re-prioritization to Web developers is an important use case. From a developer perspective, H1 also had some form of re-prioritization (e.g. viewport images in Chrome get “reprioritized” and leave the queue earlier).


#34

Request destination does not map well to priorities. e.g. “script” destination in Chrome can be mapped to “high” when it’s a blocking script at the head, “medium” when it’s a blocking script at the bottom of the page (“discovered after an image” is the heuristic used) and “low” when it’s an async script. I think we’ll need to define other form of default groups if we go that route.


#35

Can you elaborate on why is would be necessary? Currently there’s no standard mapping between how browsers perceive resource priority and H2 dependency trees. (e.g. Firefox and Chrome mark critical JS and async JS in very different ways, both in dependencies and weights)

Browsers might have different ways of using it, but there’s a standard data model - H2 priority trees (if we ignore the problem of H1 for now). If this effort introduces a different model, then we need a mapping (which is likely to be lossy).

If we’re going to abandon priority trees in favour of something else, this might make sense, but I haven’t seen serious suggestions that we do so yet.

I believe we can tackle the stated use cases by providing something simpler, such as “upgrade/downgrade” semantics as well as optional dependencies or deference indications (“critical” vs. “important” vs. “other”).

Would “upgrade” change the weight, or re-root the dependency? If the latter, where?

If we define a new model like this, my concern is that not only will different browsers have different ways of using the dependency tree, but they’ll also now have different ways of mapping this new model into it. Early days, of course, but it feels like a mess in the making.

Allowing authors to query and express dependencies (with optional weights) might be a way around this. It’s easy and intuitive to say “resource load A depends on B” and so forth.

That would work well for JS, but not for markup (as it would be pretty verbose), unless we assign some sort of semantics to ordering / containment, etc.

I’d suggest that the best way forward might be to go Extensible Web on it – i.e., provide a low-level, primitive but powerful JS API, and let libraries figure out higher-level abstractions.

I agree that defining a full mapping of resources to H2 priorities would be potentially more powerful, but: Servers will rarely have knowledge of all the resources a certain page will trigger, and won’t necessarily know the ideal dependency tree. If they do, and all said resources come from a single server (again, rare), the server could apply those ideal dependency trees while ignoring the browser’s sent priorities.

Sorry, you lost me here. I’m not suggesting that this be done statically server-side; this is client-side code running. Or are you saying that we ought only define “hints” that the client can use to adjust its priorities at runtime (but it might decide to ignore them, or only partially follow them)? If so, I wonder if we can get it right the first time, and how much work fixing it after that would take.


#36

I think there also needs to be a way to limit priorities per domain. I could see situations were third party widgets would mark all their downloads as highest priority, an embedding site considering its own priorities would get overridden or end up in a situation were everything is marked as priority 1 in order to co

Interesting. I could see each individual origin having a priority controller for its connection(s), but then an overall priority controller for the page load that balances between them (or maybe the top-level page has that ability). Complex as all get out, but powerful.


#37

I was thinking much simpler and out of direct control of the page (eg in the manifest). Say for instance Twitter’s widget decided to be evil and mark its images all as priority 1, you could decalre "resource_priority": [{ "limit_priority": 3, "domains": ["*.twitter.com"] }], and any attempts to load resources from that domain that are above a priority 3 are downgraded to a 3. Or perhaps you want to limit everything but your domains

"resource_priority": [{
	"limit_priority": 3, "domains": ["*"]
}, {
	"limit_priority": 1, domains: ["*.mydomain.com", "mydns.com"]
}]