Skip to content
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

[Blazor] Auto Render not working properly in .NET 8 and page locks until WebAssembly start #52154

Closed
1 task done
AmBplus opened this issue Nov 17, 2023 · 78 comments · Fixed by #53159
Closed
1 task done
Assignees
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. feature-full-stack-web-ui Full stack web UI with Blazor
Projects
Milestone

Comments

@AmBplus
Copy link

AmBplus commented Nov 17, 2023

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I updated Visual Studio to the latest version 17.8.0 and created a Blazor app using Auto render mode With Dotnet 8. When I run the application and navigate to the Counter tab, the whole page locks up until the WebAssembly Start. I can't do anything or switch to another tab until the download Finished And WebAssembly Start, even though it happens quickly locally. This could cause problems in production where the download may take longer. It also goes against the purpose of Auto render mode.

Expected Behavior

With auto render mode, the page should initially use server-side rendering. Once WebAssembly finishes downloading And Start, it can switch over seamlessly without locking up the page.

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

NET 8 , Windows 10-64

Anything else?

As you can see, the page is locked and prevents navigating to another tab.
lockPage

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Nov 17, 2023
@danroth27 danroth27 changed the title [Blazor] Auto Render not working properly in .NET 8 and page locks until WebAssembly downloads [Blazor] Auto Render not working properly in .NET 8 and page locks until WebAssembly start Nov 17, 2023
@danroth27
Copy link
Member

The delay is most likely due to starting up the .NET WebAssembly runtime, not the download. I believe the issue is we don't currently have a way to download the .NET WebAssembly runtime without also starting it, so this delay happens even on the first visit when using interactive server rendering. But even if we could avoid that delay on the first visit, on subsequent visits you'd still see the delay as the runtime starts up even though the runtime has been cached.

@lewing @MackinnonBuck

@carlfranklin
Copy link

I’m seeing the same thing

@ray440
Copy link

ray440 commented Nov 17, 2023

If you change the Counter title to:

<h1>Counter @(OperatingSystem.IsBrowser() ? "Wasm" : "Server")</h1>

It's kinda neat to watch the change as it happens.

The delay is expected but much longer than expected leading to a stalled UX for antsy users.

Is there a way to tell what @rendermode is?

@danroth27
Copy link
Member

Is there a way to tell what @rendermode is?

Not currently: #49401

@carlfranklin
Copy link

carlfranklin commented Nov 17, 2023 via email

@mkalinski93
Copy link

I found out that OnAfterRender actually can be an indicator that the framework and dependencies has been loaded. I think I'm going to have a root level component that will display a waiting indicator or something. My problem is that my clients will have a fast initial response from the server, but the overall hydration is taking a long time (especially on mobile end with slow connectivity)

In the end, my clients would be more confused when there's no interactivity yet established. My observation is that OnAfterRender gets executed when the framework has been loaded fully. Maybe we can get used to it.

@SteveSandersonMS
Copy link
Member

SteveSandersonMS commented Nov 20, 2023

@carlfranklin I’m seeing the same thing

Can you clarify what same thing you are seeing? The original report suggested that the UI is locked while downloading WebAssembly resources, but as far as we understand that is not the case.

If you mean that the UI is locked while the WebAssembly runtime starts (not while downloading, but after downloading), this is true and always has been since the beginning of Blazor WebAssembly. Over the years we've made it faster - it took about 1 second to start on the first version, and is down to about 350ms on recent hardware. Of course it could take longer on a low-end mobile device.

Update: I think I get what you mean now, which is that on the first visit for a given user, when it's using Server interactivity
because WebAssembly files aren't yet downloaded, there is a small pause (usually< 500ms, but depends on CPU speed) once the WebAssembly files finish downloading. Is that right?

If you change the Counter title to: <h1>Counter @(OperatingSystem.IsBrowser() ? "Wasm" : "Server")</h1> It's kinda neat to watch the change as it happens.

The switchover you'll be seeing here is between prerendered content (which will display "Server") and then live interactive content (which will display "Wasm" if the wasm resources are already downloaded and cached). What sort of delay are you seeing that is much longer than expected?

@AmBplus
Copy link
Author

AmBplus commented Nov 20, 2023

@SteveSandersonMS (usually< 500ms, but depends on CPU speed)

It seems this problem only happens in debug mode. What I mean is that it's more noticeable, not that there is no locking at all in other modes.
In debug mode with Visual Studio it takes around 6 seconds , with dotnet run it takes less than 1 sec or half sec, and in release you almost don't feel your page is locked.
It seems like we don't have any issues in production mode.

@carlfranklin
Copy link

carlfranklin commented Nov 20, 2023 via email

@ziaulhasanhamim
Copy link

ziaulhasanhamim commented Nov 22, 2023

@SteveSandersonMS I am facing the same issue. In auto interactivity mode it is supposed to connect to the server with signalr instantly. But it doesn't do that rather it waits for the wasm runtime to be loaded and then connects to the server. It destroys the whole purpose of auto interactivity. The app doesn't become interactive before wasm runtime loads unlike server mode which starts instantaneously. In my case the browser first loads webassembly files and then it connects to the server with signalr. Shouldn't it first make a signalr connection and then load the files? In my network tab, I can clearly see that wasm files are downloaded before websocket connection.

@SteveSandersonMS
Copy link
Member

In my case the browser first loads webassembly files and then it connects to the server with signalr

@ziaulhasanhamim When you use the word "loads" here, do you mean "downloads" (as in the WebAssembly files are not already cached, and you are waiting for them to be transferred over the network), or do you mean it in the sense of "starts" (as in, the WebAssembly files are already downloaded, but it takes some nonzero time to start the .NET runtime under WebAssembly, most likely ~0.5s assuming you are not debugging).

@ziaulhasanhamim
Copy link

@SteveSandersonMS I meant that it is not cached. When wasm runtime is not cached it first downloads the wasm files then connects to server through websockets. But if it has to download the wasm runtime first then what is the benefit of auto interactivity.

@AmBplus
Copy link
Author

AmBplus commented Nov 22, 2023

@ziaulhasanhamim , Try Publish And See Still Have Delay

@ziaulhasanhamim
Copy link

ziaulhasanhamim commented Nov 22, 2023

Yeah in publish delay is less. But fundamentally it's a problem because what is benefit of auto interactivity if wasm runtime needs to be downloaded first(runtime is not cached already) before websocket connection

@SteveSandersonMS
Copy link
Member

I meant that it is not cached. When wasm runtime is not cached it first downloads the wasm files then connects to server through websockets. But if it has to download the wasm runtime first then what is the benefit of auto interactivity.

If that's true it would be very surprising and a definite major bug we'd hurry to patch. However I don't think we've ever observed this happening, so it's also possible that something else is going on.

Can you clarify how you know this is what's happening? Are you talking about when running on localhost? How do you know which part of the delay comes from downloading the WebAssembly files, compared with after the files are downloaded and the WebAssembly runtime is booting up?

@ziaulhasanhamim
Copy link

ziaulhasanhamim commented Nov 22, 2023

Yes, I'm facing it on localhost. The network tab shows that first, it is downloading the whole wasm runtime and then connecting the websocket. Moreover, in the console tab Blazor websocket connection message and runtime loaded message are emitted together, and after it the page becomes interactive. Also In auto interactive mode, first load time is as bad as webassembly load time whereas in server mode it loads instantly.
My observation on the network tab in slow 3G throttle.

image

I can be totally wrong here. But in general, the Auto mode load time is not appriciatable and mostly same as wasm.

@SteveSandersonMS
Copy link
Member

Thanks @ziaulhasanhamim for the clarification. This is helpful. It's possible that this problem only surfaces in the artificial localhost+throttling case rather than in real deployments. We're investigating to confirm, and will certainly look to improve this quickly either way.

@baktay
Copy link

baktay commented Nov 24, 2023

I used the sample counter-weather code as is. I switched to counter page and clicked Click Me button immediately. Nothing happened for couple of clicks until it worked (I am not using a super fast computer).

Then, I changed counter page rendermode to InteractiveServer and refreshed the page. It worked immediately. My first immediate click worked. InteractiveAuto does not seem to render from server first time (at least not as good as InteractiveServer rendermode) when compared.

My workaround was to create the counter page as @rendermode InteractiveServer and put the Counter code into a counter component named CounterComponent at wasm pages folder. Then used: <CounterComponent @rendermode="RenderMode.InteractiveAuto">

@achmstein
Copy link

Facing the same issue.

@carlfranklin
Copy link

carlfranklin commented Nov 27, 2023

@SteveSandersonMS I published a test app to Azure at https://blazorautotest.azurewebsites.net/

It does load fast, but try to click the button before it reads "Platform: Wasm"

I'm using this technique:

<h1>Platform: @(OperatingSystem.IsBrowser() ? "Wasm" : "Server")</h1>

@SteveSandersonMS
Copy link
Member

Thanks @carlfranklin. Yes that is helpful and probably does confirm the issue.

One minor remaining detail though: I see your deployment is not configured to enable WebSockets, so it's falling back on long polling. Could you update it so it does enable WebSockets? Then assuming the same issue still repros, that would be a complete smoking gun.

@carlfranklin
Copy link

@SteveSandersonMS Done.

@SteveSandersonMS
Copy link
Member

Thanks. That does seem pretty convincing. @mkArtakMSFT I think this should be at the top of our priority list.

@mkArtakMSFT mkArtakMSFT added the bug This issue describes a behavior which is not expected - a bug. label Nov 28, 2023
@mkArtakMSFT mkArtakMSFT added this to the 8.0.x milestone Nov 28, 2023
@mkArtakMSFT mkArtakMSFT added the feature-full-stack-web-ui Full stack web UI with Blazor label Nov 28, 2023
@mkArtakMSFT mkArtakMSFT added this to 8.0.x in Servicing Nov 29, 2023
@bcookew
Copy link

bcookew commented Feb 14, 2024

Hiya,

Did this go live in 8.0.2 as expected?
I am running:

  • VS 2022 CE 17.9
  • SDK 8.0.2

Behavior seems similar / the same at least in development. But perhaps I have misunderstood the fix...
Does the fix correct the behavior without developer intervention or do we still need to modify the Blazor startup in some way?

Thanks for you efforts!

@ashishsinha24
Copy link

Same here.

@mikes-gh
Copy link
Contributor

Looks like it only got merged 10 hours ago and is labelled 8.0.3
#54011
Can someone confirm it missed 8.0.2?

@wtgodbe
Copy link
Member

wtgodbe commented Feb 14, 2024

Looks like it only got merged 10 hours ago and is labelled 8.0.3
#54011
Can someone confirm it missed 8.0.2?

That label is misleading, that PR is a mechanical one that brings in any internal fixes from the previous month. All of the commits in that PR, except for the last 2, were internal commits that were included with 8.0.2. If you browse the commit history from the v8.0.2 tag (below), you can see that the render mode fix was included in 8.0.2.

https://github.com/dotnet/aspnetcore/commits/v8.0.2/

@MackinnonBuck any thoughts on why customers might not be seeing the new behavior with the change?

@MackinnonBuck
Copy link
Member

The fix was included in 8.0.2. I confirmed this by installing the SDK on a fresh machine and testing a Blazor Web App with "Auto" interactivity by creating a new project from the project template. All that should be required for existing projects is updating Microsoft.AspNetCore.Components.* packages to use version 8.0.2.

My verification steps were the following:

  1. Create the project using dotnet new blazor -int Auto
  2. Start the project using dotnet run
  3. Navigate to the app's URL
  4. Open the browser dev tools window (Edge)
  5. In the network tab, select Fast 3G (or Slow 3G) throttling
  6. Navigate to the counter page
  7. Observe that WebAssembly resource downloads are throttled, allowing server interactivity to start before WebAssembly resources finish downloading

A few notes/caveats:

  • Especially when a debugger is attached, the time it takes to start the WebAssembly runtime may noticeably contribute to the startup delay seen during development.
  • When using the Auto render mode, blazor.boot.json has to be fetched over the network before the circuit starts. This is because blazor.boot.json contains the WebAssembly resource hash that's used to determine whether WebAssembly resources are cached. This is a step that's not required when using the InteractiveServer render mode, so it might contribute to a startup time difference between the InteractiveAuto and InteractiveServer render modes when Auto resolves to using Server interactivity.

For those that see issues not described by the above, could you describe what problem you're seeing? Thanks!

@MackinnonBuck
Copy link
Member

For those that see issues not described by the above, could you describe what problem you're seeing? Thanks!

To expand on this, if someone can point out to a difference in behavior between 8.0.2 and the fix that I linked in a previous comment, that would potentially point to a bug. Thanks!

@mikes-gh
Copy link
Contributor

mikes-gh commented Feb 14, 2024

@MackinnonBuck For me I made sure all my references were 8.0.2
I had previously tested your hotfix successfully.
As a preliminary test using 8.0.2 the problem of delayed server interactivity returns the same as before.

image

Although I thought previously without the fix the websocket came after all the libraries for the app had downloaded. It seems now only the main dotnet wasm runtime comes before the websocket so maybe the delay is something else locally like debugger startup. I probably need to setup both senarios alonside each other to be sure

@bcookew
Copy link

bcookew commented Feb 15, 2024

A small update. My experience of that delay and switchover to wasm rather than staying in interactive server has been resolved.
I think I caused my own problem by trying to implement an undocumented (unsupported) project configuration. I was experimenting with adding a second client project with the same setup as the original and then when I couldn't determine how to resolve an issue around Static Assets paths to the two projects I deleted one... You can probably see where this is going.

I thoughtlessly deleted the original client project instead of the one I added and didn't observe that there was an extra package reference.

<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.2" PrivateAssets="all" />

As soon as I realized that this was the only difference I could find between a fresh template and my project I removed it, cleaned, rebuilt, ran without debugging, and there it was. The behavior was working correctly as you had described.

Apologies for reopening this can of worms over an error on my part.

@david-at-solve
Copy link

Auto-mode now works as expected, thank you!

@danroth27
Copy link
Member

Same here.

@ashishsinha24 Can you share more details about the issue you're still seeing?

@ashishsinha24
Copy link

Same here.

@ashishsinha24 Can you share more details about the issue you're still seeing?

I am still seeing that the app locks until the wasm is downloaded. I am seeing it in debug and release versions. I am using Radzen controls. Maybe the problem is specific to this. I haven't had the time to fully test it.

@MackinnonBuck
Copy link
Member

@ashishsinha24,

I am still seeing that the app locks until the wasm is downloaded.

If the app is locking up, it's probably the WebAssembly runtime starting, not being downloaded. The WebAssembly runtime starts synchronously, which is why the webpage becomes temporarily non-interactive. Does the same thing happen when using an InteractiveWebAssembly render mode? If so, that would indicate that this is not a problem with the InteractiveAuto mode, but rather a performance characteristic of WebAssembly runtime startup.

@mikes-gh
Copy link
Contributor

@MackinnonBuck

  1. Is it expected that wasm dotnet runtime downloads and starts up before the websocket for InteractiveServer becomes available?
  2. Which exact libraries does your fix alter.

TIA

@MackinnonBuck
Copy link
Member

MackinnonBuck commented Feb 15, 2024

Is it expected that wasm dotnet runtime downloads and starts up before the websocket for InteractiveServer becomes available?

It's not expected, but I don't think this is what's happening in the screenshot you shared. If you're pointing out that dotnet.native.wasm gets downloaded before the websocket connects, that's somewhat of a timing coincidence, and the download of dotnet.native.wasm doesn't block the circuit starting up. If you test using "Slow 3G" performance, you might find that the websocket can connect before dotnet.native.wasm fully downloads. But the WebAssembly runtime doesn't actually start until all .wasm downloads have completed.

Which exact libraries does your fix alter.

The fix only alters blazor.web.js, which is an embedded resource in Microsoft.AspNetCore.Components.Endpoints.dll, which is part of the ASP.NET Core shared framework.

@Andrzej-W
Copy link

I tested a new application created as Global InteractiveAuto and everything works as expected. I can switch between pages and Counter page is interactive while wasm files are loaded.

@ashishsinha24 I have no experience with Radzen components, but I have some with DevExpress. Simple application with one DxGrid downloads A LOT of files and it is huge. Here is an interesting blog post:
https://community.devexpress.com/blogs/aspnet/archive/2023/03/16/blazor-webassembly-aot-compilation-and-link-trimming.aspx

Maybe you see the problem described by @MackinnonBuck (WebAssembly startup.) On a fast computer I have to wait 1 s. before WebAssembly app with DevExpress components will be interactive. On iPhone 6s and laptop with Intel Core i7-6500U processor I have to wait about 4 s. It is possible that app with Radzen component also needs a few seconds to start on your hardware.

@mikes-gh
Copy link
Contributor

@MackinnonBuck

But the WebAssembly runtime doesn't actually start until all .wasm downloads have completed.

If the app is locking up, it's probably the WebAssembly runtime starting, not being downloaded. The WebAssembly runtime starts synchronously, which is why the webpage becomes temporarily non-interactive. Does the same thing happen when using an InteractiveWebAssembly render mode? If so, that would indicate that this is not a problem with the InteractiveAuto mode, but rather a performance characteristic of WebAssembly runtime startup.

I'm a bit lost on the order of things. I hadn't considered that the WebAssembly runtime starting could block the Blazor Server interactivity.

So if the app downloads quite quickly then the WebAssembly runtime starting can still block the Blazor Server interactivity giving the same feel that the app is not responding even though the websocket is open?

@MackinnonBuck
Copy link
Member

So if the app downloads quite quickly then the WebAssembly runtime starting can still block the Blazor Server interactivity giving the same feel that the app is not responding even though the websocket is open?

I should have been clearer - the WebAssembly runtime (as in the code from the .Client project) won't actually start until all .wasm downloads have completed and a component requires WebAssembly interactivity. So, if you had an InteractiveServer component and InteractiveWebAssembly component on the same page, the starting of the WebAssembly runtime could momentarily block interactivity for the InteractiveServer component. But this shouldn't happen when only using the InteractiveAuto render mode. If you do see a delay like that, it's probably because the InteractiveAuto component is using WebAssembly interactivity.

@saintarian
Copy link

Thanks for the fix, @MackinnonBuck. I updated my SDK to 8.0.2 and tried on a new project and the issue looks fixed. However, I see that all the wasm files are downloaded serially (see image below). Is there a way to make this parallel (or, say 10 degrees of parallelism), while still having the websocket connection be established before the wasm is downloaded?

Screenshot 2024-02-16 at 5 25 46 PM

@Andrzej-W
Copy link

@saintarian you cannot download 10 files concurrently because your application will be unusable. Maybe it is not a problem on high speed fiber Internet connection but try to download a few files concurrently using cellular network and check latency with ping command.

@bxjg1987
Copy link

Regarding the content here, the official document does not seem to provide a detailed explanation. Would you like to supplement this document

3dots pushed a commit to 3dots/aspnetcore-Web.JS that referenced this issue Feb 19, 2024
# Auto render mode improvements

Backport of dotnet/aspnetcore#53159

Improves the Auto render mode so that components are more responsive and have a decreased initial time to interactivity when WebAssembly resources are not already cached.

## Description

One of the goals of the Auto render mode was to allow apps to become interactive as quickly as possible via Server interactivity, while WebAssembly bits were downloaded in the background for use on future visits to the site. However, since WebAssembly resources were being downloaded with maximal parallelism, the quality of the websocket connection required for Server interactivity was negatively impacted, often to the extent that the websocket wouldn't connect until WebAssembly resources had finished downloading completely, largely defeating the purpose of the Auto render mode.

This PR makes the following improvements:
* Removes a problematic timeout on loading the WebAssembly boot config. This fixes a problem where Server interactivity was always being used when the boot config took too long to load.
* Introduces a limit to the maximum parallel WebAssembly resource downloads when an Auto component initiates the startup of the WebAssembly runtime. This limit is set to 1 and overrides any user-specified limit.
* Fixes an issue where the circuit sometimes remains open even if WebAssembly gets selected for Auto interactivity.

I provided a preview of these changes in dotnet/aspnetcore#52154 (comment) so that customers could try them out, and the feedback so far has been very positive.

Fixes dotnet/aspnetcore#52154

## Customer Impact

A significant number of customers reported being affected by this problem in issues like dotnet/aspnetcore#52154. I supplied customers with a preview of the fix that they could patch it into their app, and many indicated that their problems were resolved by the fix. The Auto render mode was one of the key features released in .NET 8, so it's important that it works in the way we've been advertising.
 
## Regression?

- [ ] Yes
- [X] No

## Risk

- [ ] High
- [ ] Medium
- [X] Low

The core Auto render mode functionality is unaffected by this change - we added small tweaks to adjust the throttling amount and remove a problematic timeout. Additional tests were added to verify the changes in behavior, and we've been testing these changes manually to ensure they work well in real-world scenarios (various device types and connection qualities).

## Verification

- [X] Manual (required)
- [X] Automated

## Packaging changes reviewed?

- [ ] Yes
- [ ] No
- [X] N/A
@bcookew
Copy link

bcookew commented Feb 20, 2024

It may be unrelated and specific to something I have done in my app but... Is anyone having difficulty with InteractiveAuto since the last update when using:
@rendermode @(new InteractiveAutoRenderMode(prerender: false))
The page I am trying to render is injecting services from the WASM service collection which is why I am not pre-rendering.
Page loads no issues with
@rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
But when using auto I get nothing loading and if I navigate to another page I get a circuit exception.
image

The component loads normally if I refresh the page once or twice. Maybe a BrowserLink / Debugging issue?
I did notice that after navigating to the page the DevTools Debugger does briefly hold on an unhandled exception but even with "Pause on uncaught exceptions" ticked it jumps over it so quickly I can't catch it.

Edit:
I was not trying to use prerender = false prior to the 8.0.2 update so I don't know if this is new behavior since the changes talked about above.

@danielgreen
Copy link

@bcookew If the page initially loads in server mode then it will be trying to resolve the injected dependencies from the server side service provider. The dependencies will need to be present there, if they're not that could explain the problems.

@bcookew
Copy link

bcookew commented Feb 20, 2024

@bcookew If the page initially loads in server mode then it will be trying to resolve the injected dependencies from the server side service provider. The dependencies will need to be present there, if they're not that could explain the problems.

Good offer. That was, in truth, where I started my day. I had a look at the MS Docs related to render modes here. And followed their advice to set prerender to false. This did indeed clear the DI issue but left me with this odd behavior where the page never loads unless I refresh a couple of times.

Open to further thoughts / discussion on this for sure!

@danielgreen
Copy link

danielgreen commented Feb 20, 2024

@bcookew It still sounds like a DI issue. Setting prerender to false will still mean it tries to load in server (SignalR) mode initially. Then when you refresh, if the wasm resources have been downloaded by that point then after refreshing it will switch to wasm mode.
So you still need to ensure the dependencies can be resolved server side, or change the render mode from Auto to WebAssembly.

@bcookew
Copy link

bcookew commented Feb 20, 2024

@danielgreen
Thanks, I will dig deeper around that.

@Evengard
Copy link

I stumbled upon a similar problem. A browser which doesn't have WASM or WASM is disabled alltogether. I tested with Firefox setting javascript.options.wasm to false. I'd expect it to still work using server-side rendering. Instead, it seems like it crashes and no interactivity works.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. feature-full-stack-web-ui Full stack web UI with Blazor
Projects
Development

Successfully merging a pull request may close this issue.