Duniya

High-Performance Caching Patterns in ASP.NET Core 9

A
Desh-Duniya Team Author
| Published September 12, 2026

High-Performance Caching Patterns in ASP.NET Core 9

Building modern web applications means balancing performance, scalability, and third-party reliability. Whether you are consuming external APIs (like news feeds or IP geolocation services) or querying a SQL database, relying on real-time network calls for every request leads to high latency, rate limits, and downtime.

In this guide, we explore practical high-performance caching strategies using ASP.NET Core 9—moving beyond basic memory caching to a resilience-first architecture.


1. The Cache Stampede Problem

A naive caching implementation looks like this:

  1. Check if key exists in cache.
  2. If hit: return data.
  3. If miss: fetch from source, save to cache, and return.

Under heavy traffic, this leads to a Cache Stampede (also known as the Thundering Herd problem).

Warning: When a popular cache key expires, 100 simultaneous requests will register a cache miss at the exact same millisecond. All 100 requests will hit your API or database concurrently, causing HTTP 429 errors or server crashes.

To fix this, we ensure only one request fetches fresh data while others wait or serve fallback data.


2. Implementation with HybridCache

ASP.NET Core 9 introduced HybridCache, which unifies in-memory speed with distributed caching (e.g., Redis) while built-in thread synchronization prevents stampedes automatically.

C# Service Implementation

using Microsoft.Extensions.Caching.Hybrid;
using Microsoft.Extensions.Logging;

namespace YourAppName.Services;

public class ResilientApiService
{
    private readonly HybridCache _cache;
    private readonly ILogger<ResilientApiService> _logger;

    public ResilientApiService(HybridCache cache, ILogger<ResilientApiService> logger)
    {
        _cache = cache;
        _logger = logger;
    }

    public async ValueTask<TData?> GetCachedDataAsync<TData>(
        string cacheKey,
        Func<CancellationToken, Task<TData?>> fetchDataFunc,
        TimeSpan duration)
    {
        return await _cache.GetOrCreateAsync(
            key: cacheKey,
            factory: async cancellationToken =>
            {
                _logger.LogInformation("Cache miss for '{CacheKey}'. Fetching fresh data...", cacheKey);
                
                try
                {
                    return await fetchDataFunc(cancellationToken);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Fetch failed for '{CacheKey}'.", cacheKey);
                    return default;
                }
            },
            options: new HybridCacheEntryOptions
            {
                Expiration = duration,
                LocalCacheExpiration = duration
            }
        );
    }
}

Discussion (1)

Please sign in to participate in the discussion.
P
pawan041184@gmail.com • Sep 12, 2026 • 04:38 PM

Nice Article