AWS Developer Tools Blog

Introducing the AWS .NET Distributed Cache Provider for DynamoDB (Preview)

We are happy to announce the preview release of the AWS .NET Distributed Cache Provider for DynamoDB. This library enables Amazon DynamoDB to be used as the storage for ASP.NET Core’s distributed cache framework. A cache can improve the performance of an application; an external cache allows the data to be shared across application servers and helps to avoid cache misses when the application is restarted or redeployed.

The AWS .NET Distributed Cache Provider for DynamoDB is the successor to the AWS DynamoDB Session State Provider. Initially released in 2012, the session state provider library is only compatible with .NET Framework applications and not with ASP.NET Core.

Using the Distributed Cache Provider

Consider a hypothetical ASP.NET Core web application that displays a local weather forecast. Generating the forecast may be computationally expensive relative to rendering the page, so we want to cache the current forecast for 24 hours, as well as share the cached forecast across the multiple servers hosting our application.

To get started, first install the AWS.AspNetCore.DistributedCacheProvider package from NuGet.org. Then in your Program.cs, call AddAWSDynamoDBDistributedCache on the WebApplicationBuilder.Services collection, and configure it with an existing DynamoDB table to store the cached data:

builder.Services.AddAWSDynamoDBDistributedCache(options =>
{
    options.TableName = "weather_cache";
    options.PartitionKeyName = "id";
    options.TTLAttributeName = "cache_ttl";
});

Then in the page model you can use the injected IDistributedCache instance to load the cached forecast if it exists. Otherwise generate (and cache) a new forecast:

public class WeatherForecastModel : PageModel
{
    private readonly IDistributedCache _distributedCache;
    
    public WeatherForecastModel(IDistributedCache distributedCache)
     {
         _distributedCache = distributedCache;
     }
    
    public WeatherForecast CurrentForecast { get; internal set; }

    public async Task OnGetForecast()
    {
        // Load the previous weather forecast from the cache
        var cachedForecastBytes = await _distributedCache.GetAsync("weatherForecast");
    
        // If there was a cache entry, convert it from the cached bytes
        if (cachedForecastBytes != null)
        {
            CurrentForecast = ForecastConverter.FromBytes(cachedForecast);
        }
        else
        {
            // Compute a new forecast
            CurrentForecast = WeatherPredictor.GenerateNewForecast();
            
            // Prepare the options for the cache entry, 
            // which is set to expire in 24 hours
            var options = new DistributedCacheEntryOptions()
            {
                AbsoluteExpiration = DateTimeOffset.UtcNow.AddHours(24)
            };
            
            // Store the new forecast in the cache 
            await _distributedCache.SetAsync("weatherForecast", ForecastConverter.ToBytes(CurrentForecast), options);    
        }
    }
}

After loading this page you can see the DynamoDB item in the table, with the value attribute containing the the serialized weather forecast. The cache_ttl, ttl_deadline , and ttl_window attributes are used internally to support the various expiration settings available via DistributedCacheEntryOptions.

Viewing the cache entry for a single ASP.NET Core session’s PageCount in the Scan or query items page in the DynamoDB console

Configuration

For production applications we recommend configuring the cache provider with the following options:

  • TableName – The name of the DynamoDB table that will be used to store the cache data.
  • PartitionKeyName – The name of DynamoDB table’s partition key. If this option is not set, a DescribeTable service call will be made at startup to determine the partition key’s name.
  • TTLAttributeName – DynamoDB’s Time To Live (TTL) feature is used for removing expired cache items from the table. This option specifies the attribute name that will be used to store the TTL timestamp. If this is not set, a DescribeTimeToLive service call will be made to determine the TTL attribute’s name.

The table must use a partition key of type string and not use a sort key, otherwise an exception will be thrown during the first cache operation. DynamoDB’s Time to Live feature must be enabled for the table, otherwise expired cached entries will not be deleted.

The cached items’ partition keys will be prefixed by the configurable PartitionKeyPrefix (the default prefix is “dc:”). This may help avoid collisions if the cache entries are mixed with other items in a table, and allows you to use IAM policy conditions for fine-grained access control to limit access to just the cache entries.

You may set CreateTableIfNotExists to true to allow the library to create a table automatically if it does not already exist. This is only recommended for development or testing purposes because it requires additional permissions and adds latency to the first cache operation.

Refer to the Configuration section in the project README for the full set of options, and the Permissions section for the minimum required IAM permissions for different scenarios.

Using the Distributed Cache Provider for ASP.NET Core Session State

A common application of an IDistributedCache implementation is to store session state in an ASP.NET Core application. As opposed to the first example above, these cache entries are user-specific and tied to the session ID maintained by ASP.NET Core. Also the expiration timestamps for all of the cache entries are now controlled by the IdleTimeout option on the session configuration, as opposed to creating DistributedCacheEntryOptions for each cache entry in the above example.

To get started, again install the AWS.AspNetCore.DistributedCacheProvider package from NuGet.org. Then in your Program.cs, call AddAWSDynamoDBDistributedCache and configure it with an existing DynamoDB table, and also configure the session state behavior:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAWSDynamoDBDistributedCache(options =>
{
    options.TableName = "session_cache_table";
    options.PartitionKeyName = "id";
    options.TTLAttributeName = "ttl_date";
});

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromSeconds(90);
    options.Cookie.IsEssential = true;
});

var app = builder.Build();
app.UseSession();
...
app.Run();

Now calls to ISession methods such as Get/Set, GetString/SetString will load and save data to DynamoDB. Below is a hypothetical page that stores a user-specific page hit counter:

public class PageCountModel : PageModel
{
   private const string PageCountKey = "pageCount";
   
   public int PageCount { get; internal set; }

    public PageCountModel()
    {
    
    }

    public void OnGet()
    {
       // Load the old page count for this session, or start at 0 
       // if there isn't an existing entry in the cache
        PageCount = HttpContext.Session.GetInt32(PageCountKey) ?? 0;

        PageCount += 1;

        // Save the incremented count in the cache
        HttpContext.Session.SetInt32(PageCountKey, PageCount);
    }
}
@page
@model PageCountModel

<div class="text-center">
    Number of page views: @Model.PageCount
</div>

After loading this page once you can see the DynamoDB item in the table. As opposed to the first example above, the id and TTL attributes are managed by ASP.NET Core’s session middleware. You only need to set and get the value.

Viewing the cache entry for a single ASP.NET Core session’s PageCount in the Scan or query items page in the DynamoDB console

What’s Next?

The Distributed Cache Provider for DynamoDB is currently in preview, and development of the framework is being done in the awslabs/aws-dotnet-distributed-cache-provider GitHub repository. Download the AWS.AspNetCore.DistributedCacheProvider package from NuGet.org to try it out, and refer to the README for more documentation. Don’t hesitate to create an issue or a pull request if you have ideas for improvements

About the author:

Alex Shovlin

Alex Shovlin

Alex Shovlin is a software development engineer on the .NET SDK team at AWS. He enjoys working on projects and tools that aim to improve the developer experience. You can find him on GitHub @ashovlin.