Open Closed

[Performance] MvcCachedApplicationConfigurationClient fetches configuration and localization sequentially #10404


User avatar
0
nacho created

[Performance] MvcCachedApplicationConfigurationClient fetches configuration and localization sequentially — easy ~400ms savings on cold cache

Summary

MvcCachedApplicationConfigurationClient.GetRemoteConfigurationAsync() fetches application-configuration and application-localization sequentially, but they can safely run in parallel. The localization call depends on a culture name that is already available from CultureInfo.CurrentCulture (set by ABP's RequestLocalizationMiddleware) — it does not need to wait for the configuration response.

This is a low-risk, high-impact optimization for all tiered Blazor Server deployments.

Current behavior (sequential)

[Request starts]
  → GET /api/abp/application-configuration    ~120ms
  ← Response received
  → GET /api/abp/application-localization      ~200ms  (waits for config to finish)
  ← Response received
[Total: ~320ms]

The sequential dependency exists because the current code extracts cultureName from config.Localization.CurrentCulture.Name before calling the localization endpoint:

// Current implementation in MvcCachedApplicationConfigurationClient
private async Task<ApplicationConfigurationDto> GetRemoteConfigurationAsync()
{
    var config = await ApplicationConfigurationAppService.GetAsync(
        new ApplicationConfigurationRequestOptions { IncludeLocalizationResources = false }
    );

    // ❌ Waits for config just to get cultureName
    config.Localization.Resources = (await ApplicationLocalizationClientProxy.GetAsync(
        new ApplicationLocalizationRequestDto
        {
            CultureName = config.Localization.CurrentCulture.Name,
            OnlyDynamics = true
        }
    )).Resources;

    return config;
}

Proposed behavior (parallel)

[Request starts]
  → GET /api/abp/application-configuration    ~120ms  (parallel)
  → GET /api/abp/application-localization      ~200ms  (parallel)
  ← Both responses received
[Total: ~200ms — savings: ~120ms per cache miss]

The key insight is that CultureInfo.CurrentCulture.Name is already set by ABP's RequestLocalizationMiddleware before MvcCachedApplicationConfigurationClient is invoked. It will always match config.Localization.CurrentCulture.Name, so we can use it directly without waiting for the configuration response:

// Proposed implementation
private async Task<ApplicationConfigurationDto> GetRemoteConfigurationAsync()
{
    // CultureInfo.CurrentCulture is already set by ABP's RequestLocalizationMiddleware
    var cultureName = CultureInfo.CurrentCulture.Name;

    var configTask = ApplicationConfigurationAppService.GetAsync(
        new ApplicationConfigurationRequestOptions { IncludeLocalizationResources = false }
    );

    var localizationTask = ApplicationLocalizationClientProxy.GetAsync(
        new ApplicationLocalizationRequestDto
        {
            CultureName = cultureName,
            OnlyDynamics = true
        }
    );

    await Task.WhenAll(configTask, localizationTask);

    var config = await configTask;
    config.Localization.Resources = (await localizationTask).Resources;

    return config;
}

Measured performance impact

Tested on a Blazor Server tiered application (ABP 10.0.0 Commercial, .NET 10, localhost).

Cold cache (first request after app start)

| Metric | Sequential (before) | Parallel (after) | Savings | |--------|---------------------|-------------------|---------| | application-configuration | 124ms | 117ms | — | | application-localization | 208ms | 237ms | — | | Total wall time | 332ms (sum) | 238ms (max) | ~94ms |

Warm cache miss (authenticated user, new cache key)

| Metric | Sequential (before) | Parallel (after) | Savings | |--------|---------------------|-------------------|---------| | application-configuration | 95ms | 116ms | — | | application-localization | 73ms | 120ms | — | | Total wall time | 168ms (sum) | 121ms (max) | ~47ms |

Note: In production environments with higher latency to the API host, the savings scale proportionally. If the shorter call takes 200ms in production, you save ~200ms per cache miss.

Impact per page load

Each page load triggers this on cache miss during both prerender and interactive phases. Savings compound:

  • Cold start (no cache): ~120ms × 2 phases = ~240ms saved
  • Warm cache miss (new user/session): ~50–120ms saved

Why this is safe

  1. CultureInfo.CurrentCulture is guaranteed correct: ABP's RequestLocalizationMiddleware runs early in the pipeline and sets the culture before any application code executes. The MvcCachedApplicationConfigurationClient is invoked later (during Razor page rendering or Blazor circuit initialization), so the culture is already set.

  2. No behavioral change: The localization endpoint receives the exact same CultureName — we're just reading it from a different (but equivalent) source.

  3. No API contract change: Both endpoints are called with the same parameters as before.

  4. Cache key unchanged: The distributed cache key generation (MvcCachedApplicationConfigurationClientHelper.CreateCacheKey) is not affected.

Affected scenarios

  • Tiered Blazor Server — primary beneficiary (highest frequency of cache misses)
  • Tiered MVC — also benefits
  • Non-tiered / monolith — not affected (doesn't use MvcCachedApplicationConfigurationClient)

Workaround

Until this is addressed in ABP, applications can replace the service by implementing ICachedApplicationConfigurationClient directly with [Dependency(ReplaceServices = true)]:

[ExposeServices(typeof(ICachedApplicationConfigurationClient))]
[Dependency(ReplaceServices = true)]
public class ParallelCachedApplicationConfigurationClient
    : ICachedApplicationConfigurationClient, ITransientDependency
{
    // ... (full implementation available on request)
}

Important: The module containing this replacement must declare [DependsOn(typeof(AbpAspNetCoreMvcClientModule))] to ensure it loads after ABP's default registration.

Environment

  • ABP Framework version: 10.0.0
  • ABP Commercial version: 10.0.0
  • .NET version: 10.0
  • Hosting: Blazor Server (tiered architecture)
  • Database: MongoDB (not relevant to this issue)

1 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Thank you for the detailed information. I’ll go check it out.

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.2.0-preview. Updated on February 05, 2026, 13:24
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.