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

Permformance issue with StaticAssetDevelopmentRuntimeHandler.EnableSupport. #59673

Open
maliming opened this issue Jan 1, 2025 · 1 comment
Assignees
Labels
area-blazor Includes: Blazor, Razor Components investigate
Milestone

Comments

@maliming
Copy link

maliming commented Jan 1, 2025

When StaticAssetDevelopmentRuntimeHandler.EnableSupport is enabled in the development environment, websites with many static resources may experience significant delays.

The delay occurs primarily during the first request due to an O(n^2) loop in the UpdateEndpoints process.

Each static resource endpoint builder invokes the AttachRuntimePatching method and then the FindOriginalAsset method to iterate through all descriptors.

If there are 10,000 static files(js/css/images) under the wwwroot folder, this leads to 10,000 x 10,000 iterations on the first request.

Thanks.

if (StaticAssetDevelopmentRuntimeHandler.IsEnabled(result.IsBuildManifest, endpoints.ServiceProvider))
{
StaticAssetDevelopmentRuntimeHandler.EnableSupport(endpoints, result, environment, result.Descriptors);
}

private void UpdateEndpoints()
{
lock (_lock)
{
var endpoints = new List<Endpoint>();
foreach (var asset in _descriptors)
{
// At this point the descriptor becomes immutable.
asset.Freeze();
endpoints.Add(_endpointFactory.Create(asset, _conventions, _finallyConventions));
}
var oldCancellationTokenSource = _cancellationTokenSource;
_endpoints = endpoints;
_cancellationTokenSource = new CancellationTokenSource();
_changeToken = new CancellationChangeToken(_cancellationTokenSource.Token);
oldCancellationTokenSource?.Cancel();
oldCancellationTokenSource?.Dispose();
}
}

public void AttachRuntimePatching(EndpointBuilder builder)
{
var original = builder.RequestDelegate!;
var asset = builder.Metadata.OfType<StaticAssetDescriptor>().Single();
if (asset.HasContentEncoding())
{
// This is a compressed asset, which might get out of "sync" with the original uncompressed version.
// We are going to find the original by using the weak etag from this compressed asset and locating an asset with the same etag.
var eTag = asset.GetWeakETag();
asset = FindOriginalAsset(eTag.Tag.Value!, descriptors);

private static StaticAssetDescriptor FindOriginalAsset(string tag, List<StaticAssetDescriptor> descriptors)
{
for (var i = 0; i < descriptors.Count; i++)
{
if (descriptors[i].HasETag(tag))
{
return descriptors[i];
}
}
throw new InvalidOperationException("The original asset was not found.");
}

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Jan 1, 2025
@javiercn javiercn added this to the Backlog milestone Jan 7, 2025
@javiercn javiercn self-assigned this Jan 7, 2025
@maliming
Copy link
Author

maliming commented Jan 10, 2025

hi @javiercn

Here is a simple project that can reproduce the issue.

About 3000+ static files are in the wwwroot folder.

The app takes ~15 seconds to start.

04:22:55.053 to
04:23:10.005

StaticAssetDevelopmentTest > dotnet run
04:22:55.010 info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[62] User profile is available. 
Using '.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.
04:22:55.053 info: Program[0] Application start
04:23:10.005 info: Microsoft.Hosting.Lifetime[14] Now listening on: http://localhost:5285
04:23:10.005 info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down.
04:23:10.006 info: Microsoft.Hosting.Lifetime[0] Hosting environment: Development

After setting "ReloadStaticAssetsAtRuntime": false, There is no delay anymore.

StaticAssetDevelopmentTest > dotnet run
04:29:42.732 info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[62] User profile is available. 
Using '.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.
04:29:42.768 info: Program[0] Application start
04:29:42.859 info: Microsoft.Hosting.Lifetime[14] Now listening on: http://localhost:5285
04:29:42.859 info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down.
04:29:42.859 info: Microsoft.Hosting.Lifetime[0] Hosting environment: Development

Thank you for your help. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components investigate
Projects
None yet
Development

No branches or pull requests

2 participants