ASP.NET Core | Caching or Response caching in ASP.NET Core

Caching significantly improves the performance of an application by reducing the number of calls to actual data source. It also improves the scalability. Response caching is best suited for data that changes infrequently. Caching makes the copy of data and store it instead of generating data from original source.
Response caching headers control the response caching. ResponseCache attribute sets these caching headers with additional properties.

In-memory caching

In-memory caching uses server memory to store cached data. This type of caching is suitable for a single server or multiple servers using session affinity. Session affinity is also known as sticky sessions. Session affinity means that the requests from a client are always routed to the same server for processing.

Distributed Cache

Use a distributed cache to store data when the app is hosted in a cloud or server farm. The cache is shared across the servers that process requests. A client can submit a request that's handled by any server in the group if cached data for the client is available. ASP.NET Core works with SQL Server, Redis, and NCache distributed caches.

HybridCache

The HybridCache API bridges some gaps in the IDistributedCache and IMemoryCache APIs. HybridCache is an abstract class wth a default implementation that handles most aspects of saving to cache and retrieving from cache.


Features

HybridCache has the following features that the other APIs don't have:

  • A unified API for both in-process and out-of-process caching.

    HybridCache is designed to be a drop-in replacement for existing IDistributedCache and IMemoryCache usage, and it provides a simple API for adding new caching code. If the app has an IDistributedCache implementation, the HybridCache service uses it for secondary caching. This two-level caching strategy allows HybridCache to provide the speed of an in-memory cache and the durability of a distributed or persistent cache.

  • Stampede protection.

    Cache stampede happens when a frequently used cache entry is revoked, and too many requests try to repopulate the same cache entry at the same time. HybridCache combines concurrent operations, ensuring that all requests for a given response wait for the first request to populate the cache.

  • Configurable serialization.

    Serialization is configured as part of registering the service, with support for type-specific and generalized serializers via the WithSerializer and WithSerializerFactory methods, chained from the AddHybridCache call. By default, the service handles string and byte[] internally, and uses System.Text.Json for everything else. It can be configured for other types of serializers, such as protobuf or XML.

To see the relative simplicity of the HybridCache API, compare code that uses it to code that uses IDistributedCache. Here's an example of what using IDistributedCache looks like:

C#
public class SomeService(IDistributedCache cache)
{
    public async Task<SomeInformation> GetSomeInformationAsync
        (string name, int id, CancellationToken token = default)
    {
        var key = $"someinfo:{name}:{id}"; // Unique key for this combination.
        var bytes = await cache.GetAsync(key, token); // Try to get from cache.
        SomeInformation info;
        if (bytes is null)
        {
            // Cache miss; get the data from the real source.
            info = await SomeExpensiveOperationAsync(name, id, token);

            // Serialize and cache it.
            bytes = SomeSerializer.Serialize(info);
            await cache.SetAsync(key, bytes, token);
        }
        else
        {
            // Cache hit; deserialize it.
            info = SomeSerializer.Deserialize<SomeInformation>(bytes);
        }
        return info;
    }

    // This is the work we're trying to cache.
    private async Task<SomeInformation> SomeExpensiveOperationAsync(string name, int id,
        CancellationToken token = default)
    { /* ... */ }
}

That's a lot of work to get right each time, including things like serialization. And in the "cache miss" scenario, you could end up with multiple concurrent threads, all getting a cache miss, all fetching the underlying data, all serializing it, and all sending that data to the cache.

Here's equivalent code using HybridCache:

C#
public class SomeService(HybridCache cache)
{
    public async Task<SomeInformation> GetSomeInformationAsync
        (string name, int id, CancellationToken token = default)
    {
        return await cache.GetOrCreateAsync(
            $"someinfo:{name}:{id}", // Unique key for this entry.
            async cancel => await SomeExpensiveOperationAsync(name, id, cancel),
            token: token
        );
    }
}

The code is simpler and the library provides stampede protection and other features that IDistributedCache doesn't.

Comments

Popular posts from this blog

ASP.NET Core | Change Token

ASP.NET Core | Open Web Interface for .NET (OWIN)

How will you improve performance of ASP.NET Core Application?