Skip to content

Define the "Preload Cache" #590

Closed

Description

@yoavweiss

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)

Activity

  1. wanderview commented on Dec 18, 2017

    @wanderview
    Member

    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()?

  2. annevk commented on Dec 18, 2017

    @annevk
    Member

    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.

  3. igrigorik commented on Jan 15, 2018

    @igrigorik
    Member

    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.. :)

  4. wanderview commented on Jan 16, 2018

    @wanderview
    Member

    Given that, afaik, every implementation has one. Yes? Which is what we're after here.. :)

    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.

  5. kinu commented on Apr 11, 2018

    @kinu

    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.

  6. sleevi commented on Apr 12, 2018

    @sleevi

    @wanderview wrote:

    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.

  7. yoavweiss commented on Apr 24, 2018

    @yoavweiss
    CollaboratorAuthor

    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)

  8. yoavweiss commented on Apr 24, 2018

    @yoavweiss
    CollaboratorAuthor

    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.

  9. annevk commented on Jun 4, 2020

    @annevk
    Member

    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

  10. youennf commented on Jun 4, 2020

    @youennf
    Contributor

    I think at least that in Firefox that's what we're planning to use for rel=preload.

    I believe WebKit does the same thing.

  11. annevk commented on Jun 5, 2020

    @annevk
    Member

    whatwg/html#154 is relevant here.

  12. yutakahirano commented on Jun 5, 2020

    @yutakahirano
    Member

    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.

  13. domfarolino commented on Jun 5, 2020

    @domfarolino
    Member

    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.

  14. mayhemer commented on Jun 24, 2020

    @mayhemer

    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.

  15. annevk commented on Jun 25, 2020

    @annevk
    Member

    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?

  16. 18 remaining items

  17. noamr commented on Sep 28, 2021

    @noamr
    Contributor

    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.

    Resource Response Chrome Firefox Safari
    Load PreloadLoad PreloadLoad Preload
    Fetch Cache Multiple None Varying Multiple
    No Cache None Once None
    Error
    Fetch with `force-cache` Cache Multiple
    No Cache None Varying None
    Error Multiple
    Valid image Cache Multiple
    No Cache Once Multiple
    Error Multiple Once Multiple
    Invalid image Cache Multiple Once Multiple
    No Cache Once None
    Error
    Script Cache Multiple None Once Multiple
    No Cache Once Once Multiple
    Error Once None
    Style Cache Multiple
    No Cache Once Multiple Once Multiple
    Error Once None Once None
  18. pmeenan commented on Sep 28, 2021

    @pmeenan
    Contributor

    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.

  19. emilio commented on Sep 28, 2021

    @emilio

    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.

  20. noamr commented on Sep 28, 2021

    @noamr
    Contributor

    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).

  21. noamr commented on Sep 29, 2021

    @noamr
    Contributor

    I updated the table to include comparison between load and preload, and also fix some issues with the test.

  22. noamr commented on Oct 26, 2021

    @noamr
    Contributor

    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.

  23. added a commit that references this issue on Nov 23, 2021
  24. added a commit that references this issue on Feb 11, 2022
    8ebf2fd
  25. added a commit that references this issue on Feb 18, 2022
    8010e2c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions