AWS Developer Tools Blog
AWS .NET Distributed Cache Provider for Amazon DynamoDB now Generally Available
Today, we are excited to announce the general availability of the AWS .NET Distributed Cache Provider for Amazon DynamoDB. This is a seamless, serverless caching solution that enables .NET developers to efficiently manage their caching needs across distributed systems.
Consistent caching is a difficult problem in distributed architectures, where maintaining data integrity and performance across multiple application instances can be complex. The AWS .NET Distributed Cache Provider for Amazon DynamoDB addresses this challenge by leveraging the robust and globally distributed infrastructure of DynamoDB to provide a reliable and scalable caching mechanism.
What is the AWS .NET Distributed Cache Provider for DynamoDB?
This provider implements the ASP.NET Core IDistributedCache interface, letting you integrate the fully managed and durable infrastructure of DynamoDB into your caching layer with minimal code changes. A distributed cache can improve the performance and scalability of an ASP.NET Core app, especially when the app is hosted by a cloud service or a server farm.
Using the Distributed Cache Provider
Consider a hypothetical ASP.NET Core web application that displays a local weather forecast. Generating the forecast might be computationally expensive relative to rendering the page, so you want to cache the current forecast for 24 hours and share the cached forecast across multiple servers that host your application.
To get started, install the AWS.AspNetCore.DistributedCacheProvider package from NuGet.org. Then configure the cache provider in your Program.cs
file:
builder.Services.AddAWSDynamoDBDistributedCache(options =>
{
options.TableName = "weather_cache";
options.PartitionKeyName = "id";
options.TTLAttributeName = "cache_ttl";
});
In your webpage, leverage the injected IDistributedCache
interface to retrieve an existing cached weather forecast. If no cached forecast exists, the application generates a new forecast and stores it in the cache for future use.
// WeatherForecast.razor
@page "/weatherforecast"
@inject IDistributedCache DistributedCache
<h1>Weather Forecast</h1>
@if (CurrentForecast != null)
{
// Display your forecast data here
}
@code {
private WeatherForecast CurrentForecast;
protected override async Task OnInitializedAsync()
{
// 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(cachedForecastBytes);
}
else
{
// Compute a new forecast
CurrentForecast = WeatherPredictor.GenerateNewForecast();
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. The value
attribute contains the serialized weather forecast. The cache_ttl
, ttl_deadline
, and ttl_window
attributes are used internally to support the various expiration settings that are available in the DistributedCacheEntryOptions
object.
Configuration
For production applications, we recommend configuring the cache provider with the following options:
- TableName – The name of the DynamoDB table that is used to store the cache data.
- PartitionKeyName – The name of the partition key of the DynamoDB table. If this option is not set, a
DescribeTable
service call is made at startup to determine the name of the partition key. - TTLAttributeName – The Time To Live (TTL) feature of DynamoDB is used to remove expired cache items from a table. The TTLAttributeName option specifies the attribute name that is used to store the TTL timestamp. If this option is not set, a
DescribeTimeToLive
service call is made to determine the name of the TTL attribute.
The table must use a partition key of type string
and not use a sort key, otherwise an exception is thrown during the first cache operation. The Time to Live feature of DynamoDB must be enabled for the table, otherwise expired cached entries are not deleted.
The partition keys of cached items always start with ‘dc:’ and can be further prefixed with an additional configurable PartitionKeyPrefix
. This can help to 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 can set CreateTableIfNotExists
to true
to allow the library to create a table automatically if it doesn’t already exist. This is recommended only 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 with Hybrid Cache
What’s even more exciting is the ability to integrate this distributed cache provider with the new .NET 9 HybridCache, which provides a unified in-process and out-of-process caching solution for .NET applications.
Since computing weather forecasts can be resource-intensive, we want to cache the forecast for 15 minutes in DynamoDB. To optimize performance and reduce the load on DynamoDB, we’ll configure HybridCache
to maintain a local in-memory cache for 1 minute on each server. Configure the cache provider and hybrid cache in your Program.cs file as follows:
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
// Sets the DDB TTL entry to expire in 15 minutes
Expiration = TimeSpan.FromMinutes(15),
// Local cache expires after 1 minute
LocalCacheExpiration = TimeSpan.FromMinutes(1)
};
});
builder.Services.AddAWSDynamoDBDistributedCache(options =>
{
options.TableName = "weather_cache";
options.PartitionKeyName = "id";
options.TTLAttributeName = "cache_ttl";
});
In the page model, leverage the injected HybridCache
to retrieve an existing cached weather forecast. If no cached forecast exists, the application generates a new forecast and stores it in the cache for future use.
// WeatherForecast.razor
@page "/weatherforecast"
@inject HybridCache Cache
<h1>Weather Forecast</h1>
@if (CurrentForecast != null)
{
// Display your forecast data here
}
@code {
private WeatherForecast CurrentForecast;
protected override async Task OnInitializedAsync()
{
// Try to get the cached forecast from the HybridCache
CurrentForecast = await Cache.GetOrCreateAsync(
"weatherForecast",
async (token) =>
{
// If the forecast is not cached, generate a new one
return WeatherPredictor.GenerateNewForecast();
},
cancellationToken: CancellationToken.None
);
}
}
In this code snippet, we’re using HybridCache
to efficiently manage our weather forecast data. When GetOrCreateAsync
is called, it first checks the local in-memory cache (configured for 1-minute duration) for the fastest possible access. If the forecast isn’t found locally, it checks the distributed DynamoDB cache (configured for 15-minute duration). If the forecast isn’t found in either cache, the provided factory method generates a new forecast, which is then automatically stored in both the local and distributed caches. This tiered caching approach optimizes performance by reducing both API calls to generate new forecasts and DynamoDB requests, while ensuring that all application servers maintain reasonably fresh weather data.
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. Unlike the previous example, these cache entries are user-specific and tied to the session ID that is maintained by ASP.NET Core. In addition, the expiration timestamps for all of the cache entries are now controlled by the IdleTimeout
option on the session configuration as opposed to creating a DistributedCacheEntryOptions
object for each cache entry as is done in the previous example.
To get started, install the AWS.AspNetCore.DistributedCacheProvider package from NuGet.org. Then configure the cache provider and session state behavior in your Program.cs
file:
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, method calls on the ISession
interface that are accessible from the HttpContext such as Get/Set
and GetString/SetString
will load and save data to DynamoDB. The following is a hypothetical page that stores a user-specific page-hit counter:
// PageCount.razor
@page "/pagecount"
@inject IHttpContextAccessor HttpContextAccessor
<div class="text-center">
Number of page views: @PageCount
</div>
@code {
private int PageCount;
private const string PageCountKey = "pageCount";
protected override void OnInitialized()
{
// Load the old page count for this session, or start at 0
// if there isn't an existing entry in the cache
PageCount = HttpContextAccessor.HttpContext.Session.GetInt32(PageCountKey) ?? 0;
PageCount += 1;
// Save the incremented count in the cache
HttpContextAccessor.HttpContext.Session.SetInt32(PageCountKey, PageCount);
}
}
After loading this page once, you can see the DynamoDB item in the table. Unlike the first example shown earlier, the id
and TTL attributes are managed by the session middleware of ASP.NET Core. You only need to set and get the value.
Conclusion
In this post, we demonstrated how the AWS .NET Distributed Cache Provider for Amazon DynamoDB simplifies distributed caching for .NET developers. By leveraging the serverless infrastructure of DynamoDB, you can now easily implement high-performance, scalable caching across your applications.
Next steps:
- Explore our sample applications on GitHub
- 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: