Using Azure Cosmos DB as an ASP.NET session state and caching provider

Matias

Session state is widely used in web applications to store non-sensitive data related to user activity within the browser. ASP.NET Core has support for storing session state data across multiple providers like Redis and SQL Server. In this post, you’ll learn how to use the Azure Cosmos DB session and cache provider to leverage its low latency and global scale capabilities within your application.

What is session state?

Session state is user data that tracks a user browsing through a web application during a period of time, within the same browser. The session state expires (hence it’s limited by a period of time), and it’s limited to the interactions a particular browser is having (does not extend across browsers). It is considered ephemeral data, if it is not present it will not break the application. However, when it exists, it makes the experience faster for the user because the web application does not need to fetch it on every browser request for the same user.

It is often backed by some storage mechanism, that can in some cases, be external to the current web server and enable load balancing requests of the same browser across multiple web servers to achieve higher scalability.

The simplest session state provider is the in-memory provider that only stores data on the local web server memory and requires the application to use Application Request Routing to make the browser session be sticky to a particular web server (all requests for that browser need to always land on the same web server). This provider works well on simple scenarios but the stickiness requirement can bring load balancing problems when web applications scale.

There are a number of external storage providers available, that can store the session data in a way that can be read and accessed by multiple web servers without requiring session stickiness and enable a higher scale.

How to use the Azure Cosmos DB provider

Azure Cosmos DB can be used as a session state provider through the extension package Microsoft.Extensions.Caching.Cosmos leverages the Azure Cosmos DB .NET SDK, using a Container as an effective session storage based on a key/value approach where the key is the session identifier.

Once the package is added, you can leverage AddCosmosCache as part of your Startup process (services.AddSession and app.UseSession are common initialization steps required for any session state provider) like so:

Where you specify the database and container where you want the session state to be stored and if you want to, optionally, create them if they don’t exist.

You can customize your SDK client configuration by using the CosmosClientBuilder or if your application is already using a CosmosClient for other operations with Azure Cosmos DB, you can also inject it into the provider:

After this, you can use ASP.NET Core sessions like with any other provider and leverage the HttpContext.Session object, keep in mind to always try and load your session information asynchronously as per the ASP.NET recommendation.

Distributed cache scenarios

Given that the Azure Cosmos DB provider implements the IDistributedCache interface to act as a distributed cache provider, it can also be leveraged for any application that requires distributed cache, not just for web application requiring a performant and distributed session state provider.

Distributed caches require data consistency to provide independent instances to be able to share that cached data. When using the Cosmos DB provider you can:

  • Use your Azure Cosmos DB account in Session consistency if you can enable Application Request Routing and make requests sticky to a particular instance.
  • Use your Azure Cosmos DB account in Bounded Staleness or Strong consistency without requiring request stickiness. This provides the greatest scale in terms of load distribution across your instances.

To use the Azure Cosmos DB provider as a distributed cache, it needs to be registered in ConfiguredServices with the services.AddCosmosCache call. Once that is done, any constructor in the application can ask for the cache by referencing IDistributedCache and it will receive the instance injected by dependency injection to be used:

Troubleshooting and diagnosing

Since the Azure Cosmos DB provider uses the .NET SDK underneath, all the existing performance guidelines and troubleshooting guides apply to understanding any potential issue. Note, there is a distinct way to get access to the Diagnostics from the underlying Azure Cosmos DB operations because they cannot be exposed through the IDistributedCache APIs.

Registering the optional diagnostics delegate will allow you to capture and conditionally log any diagnostics to troubleshoot any cases like high latency:

Next steps

 

5 comments

Comments are closed. Login to edit/delete your existing comments

  • Chris Parker

    Using this for a few days and I’ve seen an increase in the reliability of our Health SAAS over using Redis Cache as a session state repository. Good job team!

  • instas tool

    Hey Everyone,
    Thank you for sharing such beautiful stuff. One thing which is confusing me is the following class created:

    public class MyBusinessClass
    {
        private readonly IDistributedCache cache;
    
        public MyBusinessClass(IDistributedCache cache)
        {
            this.cache = cache;
        }
        
        public async Task SomeOperationAsync()
        {
            string someCachedValue = await this.insta.GetStringAsync("someKey");
            /* Use the cache */
        }
    }

    Can anyone explain it to me.
    Thanks in Advance

    • Matias QuarantaMicrosoft employee

      Leaving aside the session state, .NET supports the Distributed Cache extension, that you can use in any type of application (not limited to ASP.NET application). When leveraging a Distributed Cache (through IDistributedCache), the cache gets also registered in the Dependency Injection in .NET. That means that, in any part of your application, you can pull the reference of that registered IDistributedCache in any constructor (following the Dependency Injection pattern) and use it.