Skip to content

Deterministically resolve the "auto" render mode to "server" or "webassembly" #49580

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
MackinnonBuck opened this issue Jul 21, 2023 · 7 comments · Fixed by #49858
Closed

Deterministically resolve the "auto" render mode to "server" or "webassembly" #49580

MackinnonBuck opened this issue Jul 21, 2023 · 7 comments · Fixed by #49858
Assignees
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly
Milestone

Comments

@MackinnonBuck
Copy link
Member

Currently, when the first component with an "auto" render mode is added to the page, we use a timeout-based mechanism to make a guess about whether WebAssembly resources are cached. This is because information about which resources are already cached is not yet exposed by dotnet.js. In order for the render mode to be resolved deterministically, we'll need dotnet.js to expose that information to Blazor.

@MackinnonBuck MackinnonBuck added area-blazor Includes: Blazor, Razor Components feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly labels Jul 21, 2023
@MackinnonBuck MackinnonBuck added this to the 8.0-rc1 milestone Jul 21, 2023
@mkArtakMSFT mkArtakMSFT added the enhancement This issue represents an ask for new feature or an enhancement to an existing one label Jul 31, 2023
@MackinnonBuck
Copy link
Member Author

cc @pavelsavara @lewing

@pavelsavara
Copy link
Member

  • Currently we use browser Cache but going forward we would like to drop it in favor HTTP layer cache by adding hash to assembly names in SWA.
  • blazor.boot.json now contains resources.hash of all resources
  • dotnet.js is now just small loader, it could have API which could "estimate" if the app with the same hash already loaded before. It could not guarantee HTTP cache of the browser.

@MackinnonBuck
Copy link
Member Author

@pavelsavara I wonder if dotnet.js has to be the one to perform the hash comparison. Do you think we could utilize the onConfigLoaded callback from blazor.*.js to compare the hash with any previously seen hash (maybe persisted in localStorage)?

@pavelsavara
Copy link
Member

Yes, you can do that.
You don't have way how to stop the runtime from loading at that point, tho. That would need new API.

The runtime is awaiting a result of the onConfigLoaded callback, so you can return a Promise and delay the rest of the runtime downloads and startup.

This way you could keep the bandwidth for the rest of the server page to load first.

You will need to perf-test that to know if that's good idea.

@MackinnonBuck
Copy link
Member Author

I had a brief discussion with @SteveSandersonMS and @javiercn this morning about the pros and cons of relying on resources.hash to estimate whether WebAssembly resources are cached, rather than checking the cache explicitly. It seems like we have a choice between:

  1. Having the freedom to eliminate internal use of the CacheStorage API and rely on HTTP caching
  2. Having a totally deterministic auto mode decision

I think the biggest unknown here is the level of accuracy that the hashing approach gives us. From how I understand the problem, the concerns around why the approach is only a "best guess" are that:

  1. We could download resources over the network without caching them (so, subsequent load times might still be high)
  2. The browser could evict cache entries, causing us to think resources are cached when they aren't

I don't think concern 1 is an issue because if we relied on all resources being cached, we might never resolve auto mode to WebAssembly (e.g., maybe the developer deliberately added request headers to disable caching for specific resources). I think it's generally fine and expected that Blazor WebAssembly will be used after WebAssembly resources have loaded at least once and we've cached as much as we can.

Regarding concern 2, I don't think there will ever be a case where we have a matching resource hash, but a subset (or all) WebAssembly resources have been evicted from the cache. The MDN Web Docs suggest that when browsers evict an origin's data, they evict all the data at once to avoid "inconsistency problems". From this, I believe we can derive that if WebAssembly resources are cleared, so will the localStorage entry containing the resource hash. Furthermore, research from the Chromium team indicates that browsers rarely clear cache without a user doing to deliberately.

Therefore, my proposal is that we go with the approach that relies on resources.hash to determine whether auto mode should be used.

Thoughts? Did I miss anything?

@SteveSandersonMS
Copy link
Member

This sounds like a reasonable tradeoff to me. It sounds like the failure modes would be:

  • False positive (we think files are cached but they aren't). This could be mitigated by retaining the timeout as a escape hatch. It could be a longer timeout - maybe 3 seconds, for example.
  • False negative (we think files aren't cached but they are). This would happen if localStorage was cleared (which is something an app developer may choose to do because they make unrelated use of localStorage and need it to be reset). The behavior is: we'd use Server, and in the background would instantly load all the wasm resources for free since they are already cached, and then leave a flag telling us to use Wasm next time.

Both of these are not as ideal as if we had a real way to check what is cached, so if we were retaining the CacheStorage usage it would make more sense just to use what we know from that.

I'm OK with either:

  • Doing it truly based on what's in CacheStorage since that is how it works today
  • Or, using the hash-in-localStorage approach with a timeout for the false negative case, since it's more flexible in the future

@pavelsavara
Copy link
Member

Both of these are not as ideal as if we had a real way to check what is cached

At that point we have the resource.hash we also already have blazor.boot.json downloaded.
That means we could do "quick" inventory of the Cache.

But not inventory of the HTTP cache.
The fetch only-if-cached is not implemented widely yet

Let's go with resource.hash and we could improve later.

@ghost ghost locked as resolved and limited conversation to collaborators Sep 8, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants