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
.
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.
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: