dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.19k stars 9.93k forks source link

[Blazor] Large number of elements triggers error #6411

Closed dmorrison42 closed 5 years ago

dmorrison42 commented 5 years ago

Problem

I was playing with some of the upcoming server-side Blazor features and ran into an issue.

When creating a table with many elements (around 400), an error occurs, and the number of elements is capped.

(The following error is shown on the console)

blazor.server.js:23 Uncaught TypeError: Cannot read property 'Symbol()' of undefined
    at a (blazor.server.js:23)
    at Object.t.getLogicalChild (blazor.server.js:23)
    at e.applyEdits (blazor.server.js:23)
    at e.updateComponent (blazor.server.js:23)
    at Object.t.renderBatch (blazor.server.js:16)
    at e.<anonymous> (blazor.server.js:38)
    at blazor.server.js:16
    at Array.forEach (<anonymous>)
    at e.invokeClientMethod (blazor.server.js:16)
    at e.processIncomingData (blazor.server.js:16)

(Tested on dotnet 2.1.403)

To Reproduce

Download and run https://github.com/dmorrison42/BlazorLargeNumberOfElements

(Details)

  1. Generate new project: dotnet new blazorserverside -o LargeNumberOfElements
  2. Change the OnInitAsync function in the FetchData.cshtml page to the following:
    protected override async Task OnInitAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
        while (true) {
            forecasts = (await ForecastService.GetForecastAsync(DateTime.Now))
                .Concat(forecasts ?? new WeatherForecast[0])
                .Take(1000)
                .ToArray();
            StateHasChanged();
            await Task.Delay(10);
        };
    }
  3. Build and Run
  4. Open the /FetchData page in a browser

Expected behavior

The number of elements is not capped.

Additional context

.NET Core SDK (reflecting any global.json):
 Version:   2.1.403
 Commit:    04e15494b6

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.17134
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.1.403\

Host (useful for support):
  Version: 2.1.5
  Commit:  290303f510

.NET Core SDKs installed:
  2.1.403 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download
Andrzej-W commented 5 years ago

I have made a few simple modifications in your code (see comments below). New line above HTML table to display current table size:

<p>Table size: @forecasts.Length</p>
@functions {
    WeatherForecast[] forecasts = new WeatherForecast[0];  // allocate table here, so I don't have to check if forecasts is null

    protected override async Task OnInitAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
        while (true) {
            forecasts = (await ForecastService.GetForecastAsync(DateTime.Now))
                .Concat(forecasts)
                .Take(5000)  // BIGGER TABLE
                .ToArray();
            StateHasChanged();
            await Task.Delay(10);
        };
    }
}

I have Blazor 0.7, .NET Core SDK 2.1.502, Win 10 1709, Core i7 4,2GHz, 32GB RAM

Firefox 64.0 x64 Works perfectly with table size ~1500 elements, then it slowdowns, but I was able to generate 5000 elements without errors. At this time Firefox process allocated 6GB of RAM and was almost unusable. I was able to click on Counter link and after a few seconds Firefox was usable again with only ~200MB of allocated RAM.

Chrome 71.0.3578.98 x64 First error is at ~950 elements. Usually I'm able to generate 4980, 4990 and sometimes 5000 elements. At this time I have almost 300 errors in browser console. Chrome is much faster, it slowdowns above 4000 elements, it allocates only about 500MB of memory, but this memory is not released.

Edge 16.16299 It is unacceptably slow from the beginning. Usually I'm able to generate ~400 elements.

Other observations

This bug is similar to https://github.com/aspnet/Blazor/issues/1223. It was closed by @SteveSandersonMS month ago.

Edge

dmorrison42 commented 5 years ago

I was able to try on 3.0.100-preview-009812, and similar things are happening, but the more I play with it the more confused I get.

If I measure the number of rows the way @Andrzej-W did, it doesn't seem to match the dom (not his fault, I think it's just a further symptom of my issue). If I try to use JS interop to measure the number of rows, it slows to a crawl, but seems to keep the right number of rows for longer. Yay Heisenbug!

Hopefully I'll have a couple of hours soon to get a concrete sample of my new findings.

javiercn commented 5 years ago

A few comments here. I tried this on client side and server-side blazor. The code in here is wrong.

@functions {
    WeatherForecast[] forecasts;

    protected override async Task OnInitAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
        while (true) {
            forecasts = (await ForecastService.GetForecastAsync(DateTime.Now))
                .Concat(forecasts ?? new WeatherForecast[0])
                .Take(1000)
                .ToArray();
            StateHasChanged();
            await Task.Delay(10);
        };
    }
}

To achieve the same thing you would do it as follows.

@functions {
    WeatherForecast[] forecasts;
    Task _t;
        _t = Task.Run(async () =>
        {
            while (true)
            {
                forecasts = (await ForecastService.GetForecastAsync(DateTime.Now))
                    .Concat(forecasts ?? new WeatherForecast[0])
                    .Take(100000)
                    .ToArray();
                await Invoke(() => StateHasChanged());
                await Task.Delay(2);
            };
        });
    }
}

Having an OnInitAsync event that doesn't finish is plain wrong.

After that (as you can see in my changed code) I put both firefox/chrome/edge to render up to 100000 elements in a loop and I can tell you that I haven't been able to repro this issue in any of them.

Given this, I'm closing the issue as it seems that at most, is caused by a programming error.