You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We need to define the preload cache, as currently it is not defined and different implementations are doing observably different things.
This issue was opened on the Preload spec, but as the cache would mostly sit inside Fetch and it's not clear what action would be needed on the preload spec side, I'm "moving" it here.
Related previous discussion here was at #354 where @jakearchibald made a proposal to address this. I open this as a separate issue as I think the H2 push cache and the preload cache are inherently different things in different layers.
I think that anything we define here should, to some extent, look at how the different implementations tackle this today. The logic that they apply for this (e.g. in Blink or in WebKit) seems a bit complex, but was created for the "memory cache" case. We need to think if it can be safely simplified for the preload case. (and if memory cache itself should be standardized)
What preload cache behavior is desired when document.open() is called? It seems the document is preserved, but "reset" in this case. The spec and firefox also create a new global. Should preload cache be cleared or preserved across document.open()?
I think we should just pick something and make sure it's tested. If we can get away with not creating a new global for document.open() (which would simplify matters quite a bit, as a document can then only ever be associated with a single global) it would probably be most natural that the cache is kept.
I open this as a separate issue as I think the H2 push cache and the preload cache are inherently different things in different layers.
From a developer ergonomics perspective, it would be nice if that wasn't the case. As it stands, there are subtle (potentially) observable differences in behavior between pushing and preloading a resource — one ends up in h2 push cache, other in renderer's memory cache, at least for Chrome. It would be nice to normalize all this, for everyone's sanity.. if we can.
(and if memory cache itself should be standardized)
Given that, afaik, every implementation has one. Yes? Which is what we're after here.. :)
Wanted to know if people expect that preload cache we're trying to define here also covers the prefetch case or not. From impl perspective they sit at different layers and behave slightly different (i.e. they have different lifetime), but from developers perspective the behavior may look pretty similar.
To give more context Chromium's current impl is like this: preload cache sits next to a memory cache, i.e. it's above the network stack and also above SW. Prefetch basically just puts things in HTTP cache (and never put them in memory cache or preload cache equivalent thing in Chromium). Both preload and prefetch allow subsequent fetches to succeed regardless of the Cache control up to a certain period, but that effect doesn't stick across different navigations / different fetch groups for preload, while it does for prefetch.
Do they sit above or below the network stack where service worker interception occurs?
SW sits above the http cache and below the image cache in the spec today.
The H/2 PUSH cache sits above the network and above the HTTP cache, below SW.
The Preload cache sits above SW and below/beside the image cache.
The H/2 PUSH cache sits above the network and above the HTTP cache, below SW.
I think it's conceptually below the HTTP cache at least as implemented today (as resources don't get committed to the HTTP cache until they are "claimed" from the H2 push cache)
Looking back at this, I think this work was simplified by @yutakahirano's recent(ish) refactoring of Chromium's implementation, which split the preload cache from the memory cache.
I'll try to find the time in the next few days to document what the current Chromium logic is.
Oh, I missed that the memory cache and the preload cache got separated. It seemed kinda preferable to me if all these mechanisms would end up in the same cache. I think at least that in Firefox that's what we're planning to use for rel=preload.
The memory cache in blink is shared across documents but I think the preload cache should be per-document. I expect more predictability for the preload cache than the memory cache, and that's why we have the separate caches.
Yutaka makes a good point. Our memory cache is a map of weak references, while our preload cache has strong references. This seems pretty reasonable to me.
It seemed kinda preferable to me if all these mechanisms would end up in the same cache. I think at least that in Firefox that's what we're planning to use for rel=preload.
I think separation is better.
In Gecko, there is a concept of resource memory cache, which each resource loader implements on its own (= Memory Cache). There is also a concept of sharing e.g. stylesheets among documents, this is also part of this Memory Cache concept. This is independent of preloads.
Then, each DOM document instance keeps a strong map of preloads that consuming tags can look for and consume (and remove from the map). In reality, a preload creates an entry in the Memory Cache (in resource loaders), because preload in Gecko is nothing else than a speculative load with just a flag for higher priority. The map in the document (= Preload Cache) is there to have a central spot to look at when <link rel=preload> HTML tag is added to the tree to be source of event notifications and also simplifies few other things regarding implementation, e.g. for fetch and font types, and few other details.
Are they fully separate? If you have <link rel=stylesheet href=x>...<link rel=preload href=x as=style>, what happens? How many fetches does the service worker see?
I created a table summarizing the current browser behavior, based on this test.
The test fetches different resources with different response types (Cache enabled/disabled or 404 error).
The values in the table mean the following:
None: preloaded resource is not reused.
Multiple: preloaded resource is used for subsequent requests.
Once: preloaded resource is used for one subsequent request, the next one will re-fetch the resource.
It's a bit difficult to tell which of this comes from behavior of preload vs. from behavior of the HTTP cache, but that difficulty is currently passed on to web developers - when browsers deal with cache so differently for simple use cases, it's difficult to understand how to optimize a website.
I think I'm most surprised with the "Once" result for Chrome a valid image with cache enabled (and somewhat surprised that different content types behave differently given the fetch path in Chrome). I'd have expected the resource to land in the disk cache and be re-used (though I guess that's the point of this discussion, to get the behaviors to make sense).
The "multiple" results from no-cache feel like they will likely break developer expectations.
I think the "multiple" results for no-cache for images at least come from https://html.spec.whatwg.org/#the-list-of-available-images, and I think generally they are needed for compat (e.g., different CSS image loads from a style change should result in the same image).
For stylesheets in the same document it's also long-standing behavior of Gecko at least.
I think the "multiple" results for no-cache for images at least come from https://html.spec.whatwg.org/#the-list-of-available-images, and I think generally they are needed for compat (e.g., different CSS image loads from a style change should result in the same image).
For stylesheets in the same document it's also long-standing behavior of Gecko at least.
Yea, that makes sense to me. I'm surprised of the "once" behavior in Chrome though, seems like preload does something extra that counteracts that memory cache.
Note also that the tests activate many moving parts, there could always be some fragile mistake in them (those fragile mistakes unfortunately also happen in websites that try to take advantage of preload, unfortunately).
After the TPAC conversation, this is the rough definition of preload cache I propose:
A document has a store of preloaded responses
A preload link (tag or header) fetches and keeps the response in that store (regardless of cache or errors)
The next subsequent fetch that matches that response will receive that response and remove it from store (though it might still be kept in other caches, like the resource/memory cache or HTTP cache)
The load event clears the store and reports what preloads were unused
This definition totally separates preloads from the different resource caches or any type-specific behavior, though implementations are welcome to further optimize.
Loading once and before the load event reduces issues with cache headers and reloading due to errors, and focuses preload on "loading something before it's used".
The tests will ensure that there are no extra network fetches (e.g, in case of invalid images), but they will allow implementations to have less fetches.
Activity
wanderview commentedon Dec 18, 2017
What preload cache behavior is desired when
document.open()is called? It seems the document is preserved, but "reset" in this case. The spec and firefox also create a new global. Should preload cache be cleared or preserved acrossdocument.open()?annevk commentedon Dec 18, 2017
I think we should just pick something and make sure it's tested. If we can get away with not creating a new global for
document.open()(which would simplify matters quite a bit, as a document can then only ever be associated with a single global) it would probably be most natural that the cache is kept.igrigorik commentedon Jan 15, 2018
From a developer ergonomics perspective, it would be nice if that wasn't the case. As it stands, there are subtle (potentially) observable differences in behavior between pushing and preloading a resource — one ends up in h2 push cache, other in renderer's memory cache, at least for Chrome. It would be nice to normalize all this, for everyone's sanity.. if we can.
Given that, afaik, every implementation has one. Yes? Which is what we're after here.. :)
wanderview commentedon Jan 16, 2018
Gecko at least does not have a memory cache like chromium. We have things like image cache, but that is defined in the html spec.
From my perspective the thing I want to know about these caches is:
Do they sit above or below the network stack where service worker interception occurs?
SW sits above the http cache and below the image cache in the spec today.
kinu commentedon Apr 11, 2018
Wanted to know if people expect that
preload cachewe're trying to define here also covers theprefetchcase or not. From impl perspective they sit at different layers and behave slightly different (i.e. they have different lifetime), but from developers perspective the behavior may look pretty similar.To give more context Chromium's current impl is like this: preload cache sits next to a memory cache, i.e. it's above the network stack and also above SW. Prefetch basically just puts things in HTTP cache (and never put them in memory cache or
preload cacheequivalent thing in Chromium). Both preload and prefetch allow subsequent fetches to succeed regardless of the Cache control up to a certain period, but that effect doesn't stick across different navigations / different fetch groups for preload, while it does for prefetch.sleevi commentedon Apr 12, 2018
@wanderview wrote:
The H/2 PUSH cache sits above the network and above the HTTP cache, below SW.
The Preload cache sits above SW and below/beside the image cache.
yoavweiss commentedon Apr 24, 2018
I think it's conceptually below the HTTP cache at least as implemented today (as resources don't get committed to the HTTP cache until they are "claimed" from the H2 push cache)
yoavweiss commentedon Apr 24, 2018
Looking back at this, I think this work was simplified by @yutakahirano's recent(ish) refactoring of Chromium's implementation, which split the preload cache from the memory cache.
I'll try to find the time in the next few days to document what the current Chromium logic is.
annevk commentedon Jun 4, 2020
Oh, I missed that the memory cache and the preload cache got separated. It seemed kinda preferable to me if all these mechanisms would end up in the same cache. I think at least that in Firefox that's what we're planning to use for
rel=preload.cc @smaug---- @emilio @mayhemer @ddragana
youennf commentedon Jun 4, 2020
I believe WebKit does the same thing.
annevk commentedon Jun 5, 2020
whatwg/html#154 is relevant here.
yutakahirano commentedon Jun 5, 2020
The memory cache in blink is shared across documents but I think the preload cache should be per-document. I expect more predictability for the preload cache than the memory cache, and that's why we have the separate caches.
domfarolino commentedon Jun 5, 2020
Yutaka makes a good point. Our memory cache is a map of weak references, while our preload cache has strong references. This seems pretty reasonable to me.
mayhemer commentedon Jun 24, 2020
I think separation is better.
In Gecko, there is a concept of resource memory cache, which each resource loader implements on its own (= Memory Cache). There is also a concept of sharing e.g. stylesheets among documents, this is also part of this Memory Cache concept. This is independent of preloads.
Then, each DOM document instance keeps a strong map of preloads that consuming tags can look for and consume (and remove from the map). In reality, a preload creates an entry in the Memory Cache (in resource loaders), because preload in Gecko is nothing else than a speculative load with just a flag for higher priority. The map in the document (= Preload Cache) is there to have a central spot to look at when
<link rel=preload>HTML tag is added to the tree to be source of event notifications and also simplifies few other things regarding implementation, e.g. for fetch and font types, and few other details.annevk commentedon Jun 25, 2020
Are they fully separate? If you have
<link rel=stylesheet href=x>...<link rel=preload href=x as=style>, what happens? How many fetches does the service worker see?18 remaining items
noamr commentedon Sep 28, 2021
I created a table summarizing the current browser behavior, based on this test.
The test fetches different resources with different response types (Cache enabled/disabled or 404 error).
The values in the table mean the following:
It's a bit difficult to tell which of this comes from behavior of preload vs. from behavior of the HTTP cache, but that difficulty is currently passed on to web developers - when browsers deal with cache so differently for simple use cases, it's difficult to understand how to optimize a website.
pmeenan commentedon Sep 28, 2021
I think I'm most surprised with the "Once" result for Chrome a valid image with cache enabled (and somewhat surprised that different content types behave differently given the fetch path in Chrome). I'd have expected the resource to land in the disk cache and be re-used (though I guess that's the point of this discussion, to get the behaviors to make sense).
The "multiple" results from no-cache feel like they will likely break developer expectations.
emilio commentedon Sep 28, 2021
I think the "multiple" results for no-cache for images at least come from https://html.spec.whatwg.org/#the-list-of-available-images, and I think generally they are needed for compat (e.g., different CSS image loads from a style change should result in the same image).
For stylesheets in the same document it's also long-standing behavior of Gecko at least.
noamr commentedon Sep 28, 2021
Yea, that makes sense to me. I'm surprised of the "once" behavior in Chrome though, seems like preload does something extra that counteracts that memory cache.
Note also that the tests activate many moving parts, there could always be some fragile mistake in them (those fragile mistakes unfortunately also happen in websites that try to take advantage of preload, unfortunately).
noamr commentedon Sep 29, 2021
I updated the table to include comparison between load and preload, and also fix some issues with the test.
noamr commentedon Oct 26, 2021
After the TPAC conversation, this is the rough definition of preload cache I propose:
loadevent clears the store and reports what preloads were unusedThis definition totally separates preloads from the different resource caches or any type-specific behavior, though implementations are welcome to further optimize.
Loading once and before the load event reduces issues with cache headers and reloading due to errors, and focuses preload on "loading something before it's used".
The tests will ensure that there are no extra network fetches (e.g, in case of invalid images), but they will allow implementations to have less fetches.
Define storage of preloaded resources
Define storage of preloaded resources
Define storage of preloaded resources
Define storage of preloaded resources
Define behavior of <link rel=preload> in detail
<link rel=preload> integration
<link rel=preload> integration
Define behavior of <link rel=preload> in detail