postgresqldockerblazor-server-sidemudblazorcircuit

Blazor server disconnect, error fetching data


Good day.

I have been trying to get a site up and running with Blazor and Mudblazor for portfolio reasons, nothing really fancy. Just added a few todoapp modules and forum pages for my friends and usability. Im still working on improving the site.

It uses postgresql for db connection to showcase the projects, todomodules or forum pages. I got the app running on a digital ocean droplet, 1 GB RAM in a docker container.

Even if two people try to activate a query, i get server disconnects. Even if I change pages before a page loads, server disconnects. It reconnects quickly after 3-5 seconds but it happens too frequently that the site is unusable. Query is not too complicated, we do much complicated stuff on Windows servers where I work with at least 50 users, even more on ERP. But im recently getting into Blazor and its really a mess.

We are speaking of 8 projects, about 30 images with self-loading, 5 tags, 8 users etc. Its a very simple CRUD page for showcasing projects. Query basically freezes, does not cancel itself and kills the websocket connection. As soon as connection is reestablished automatically, it continues working.

The query is as follows:

protected override async Task OnParametersSetAsync()
{
    cts.Cancel();

    cts = new CancellationTokenSource();
    filterText = "";

    try
    {
        await Task.WhenAll(
            LoadAssociatedData(cts.Token),
            LoadProjects(cts.Token)
        );
    }
    catch (Exception ex)
    {
        await ToastService.ShowErrorAsync($"Error loading Data: {ex.Message}");
    }

  private async Task LoadAssociatedData(CancellationToken token)
  {
    loading = true;
    try
    {
      await using var ctx = await DbContextFactory.CreateDbContextAsync();
      currentProjectType = await ctx.ProjectTypes
                                 .FirstOrDefaultAsync(x => x.ID == ProjectTypeId, token)
                               ?? new Project_Type();
      allImages = await ctx.Images.AsNoTracking().ToListAsync(token);
      allTags   = await ctx.Tags.AsNoTracking().ToListAsync(token);
      allUsers  = await ctx.Users.AsNoTracking().ToListAsync(token);
    }
    catch (OperationCanceledException) { }
  }

  private async Task LoadProjects(CancellationToken token)
  {
    try
    {
// images saved on db and only their ID is pulled through a junction table to avoid 
// large data transfer, and self loaded with a controller.

      await using var ctx = await DbContextFactory.CreateDbContextAsync();
      allProjects = await ctx.Projects
                             .AsNoTracking()
                             .Include(p => p.Images) 
                             .Include(p => p.Tags).ThenInclude(pt => pt.Tag)
                             .Include(p => p.Users).ThenInclude(u => u.User)
                             .Include(p => p.Type)
                             .Where(p => p.ProjectTypeID == ProjectTypeId)
                             .OrderBy(p => p.ProjectDisplayOrder ?? int.MaxValue)
                             .ToListAsync(token);
      projects = allProjects;
    }
    catch (OperationCanceledException) { }
    catch (Exception ex) when (!token.IsCancellationRequested)
    {
      await ToastService.ShowErrorAsync($"Error loading projects: {ex.Message}");
    }
    finally { loading = false; }
  }

I have seen similar topics on stack overflow but not a definitive answer regarding this issue. Adding a topic just for familiarity but no definitive solution for my problem:

Blazor server error 400 fails to negotiate to server when using routing

If you require any further code blocks for more info, i would be glad to provide. Thank you.


Solution

  • Problem is solved. Turns out I was pulling all the images for lookup.

    allImages = await ctx.Images.AsNoTracking().ToListAsync(token);
    
    allImages = await ctx.Images
       .AsNoTracking()
       .Select(img => new Fatalix_Project_Image 
       { 
           Id = img.Id, 
           OriginalFileName = img.OriginalFileName 
       })
       .ToListAsync(token);
    

    This was more than enough. It was loading more than 20 MBs of image data every time I swap to a project display page. The ImageData should never be loaded.

    In this sense I was hoping the cancellation token would save me the trouble, but turns out it does not.

    Currently the blazor page is functioning perfectly, as each image is loaded through a controller instead of a linq query.

    [HttpGet("{id}")]
    public async Task<IActionResult> GetImage(int id)
    {
        var image = await _context.Images.FindAsync(id);
        if (image == null || image.ImageData == null)
            return NotFound();
    
        var contentType = "image/jpeg";
        if (image.OriginalFileName.EndsWith(".png")) contentType = "image/png";
        else if (image.OriginalFileName.EndsWith(".gif")) contentType = "image/gif";
        return File(image.ImageData, contentType);
    }