NCache as RateLimiting Provider
NCache RateLimiting integration provides distributed rate limiter policies for ASP.NET Core applications. It extends the ASP.NET Core Rate Limiting middleware by storing limiter state in NCache, allowing multiple application instances to enforce common request limits in a distributed environment.
The integration provides NCache-backed registration methods for the following limiter types:
AddNCacheConcurrencyLimiterAddNCacheFixedWindowLimiterAddNCacheTokenBucketLimiterAddNCacheSlidingWindowLimiter
Important
In distributed deployments, keep the complete rate limiting configuration consistent across all application servers that enforce the same policy.
Each limiter can be configured using either:
- Code-based configuration in Program.cs
- Configuration-file-based settings through
IConfigurationSection
Both configuration approaches use the same underlying registration logic.
Note
Policy names must be unique for each registered rate limiting policy.
Important
Every request matching a given policy resolves to the same policy-name partition. This integration does not perform per-client, per-IP, or per-request partitioning by default.
Prerequisites
Before configuring NCache as a RateLimiting provider, make sure the following prerequisites are fulfilled:
- Install the following NuGet package in your application:
- Include the following namespaces in your application:
Microsoft.AspNetCore.RateLimitingNCache.OSS.RateLimiting
- The cache must be running.
Method 1: Using Code-Based Configuration
In this approach, limiter settings are configured directly in Program.cs by passing an Action<TOptions> delegate to the required NCache limiter registration method.
The following example registers all four NCache-backed limiter types.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(options =>
{
options.AddNCacheConcurrencyLimiter("concurrencyPolicy", cfg =>
{
cfg.PermitLimit = 10;
cfg.QueueLimit = 5;
cfg.TryDequeuePeriod = TimeSpan.FromSeconds(1);
cfg.ExpectedRequestTimeout = TimeSpan.FromSeconds(60);
cfg.LockTimeout = TimeSpan.FromSeconds(10);
cfg.CacheName = "demoCache";
});
options.AddNCacheFixedWindowLimiter("fixedWindowPolicy", cfg =>
{
cfg.PermitLimit = 100;
cfg.Window = TimeSpan.FromMinutes(1);
cfg.LockTimeout = TimeSpan.FromSeconds(10);
cfg.CacheName = "demoCache";
});
options.AddNCacheTokenBucketLimiter("tokenBucketPolicy", cfg =>
{
cfg.TokenLimit = 20;
cfg.TokensPerPeriod = 5;
cfg.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
cfg.LockTimeout = TimeSpan.FromSeconds(5);
cfg.CacheName = "demoCache";
});
options.AddNCacheSlidingWindowLimiter("slidingWindowPolicy", cfg =>
{
cfg.PermitLimit = 50;
cfg.Window = TimeSpan.FromMinutes(2);
cfg.LockTimeout = TimeSpan.FromSeconds(5);
cfg.CacheName = "demoCache";
});
});
var app = builder.Build();
app.UseRateLimiter();
app.Run();
The code above registers four named rate limiting policies. Each policy uses NCache as the distributed store for tracking permits, queues, tokens, or request windows depending on the limiter type.
Method 2: Using appsettings.json
In this approach, limiter settings are stored in appsettings.json and bound to the required limiter options by passing an IConfigurationSection to the NCache limiter registration method.
This approach is useful when rate limit settings need to vary between deployment environments without changing application code.
Step 1: Configure Rate Limiting Settings
Add the required limiter settings to appsettings.json.
{
"RateLimiting": {
"Concurrency": {
"PermitLimit": 10,
"QueueLimit": 5,
"TryDequeuePeriod": "00:00:01",
"ExpectedRequestTimeout": "00:01:00",
"LockTimeout": "00:00:10",
"CacheName": "demoCache"
},
"FixedWindow": {
"PermitLimit": 100,
"Window": "00:01:00",
"LockTimeout": "00:00:10",
"CacheName": "demoCache"
},
"TokenBucket": {
"TokenLimit": 20,
"TokensPerPeriod": 5,
"ReplenishmentPeriod": "00:00:10",
"LockTimeout": "00:00:05",
"CacheName": "demoCache"
},
"SlidingWindow": {
"PermitLimit": 50,
"Window": "00:02:00",
"LockTimeout": "00:00:05",
"CacheName": "demoCache"
}
}
}
Step 2: Register Configuration Sections
After defining the limiter settings, register the NCache rate limiting policies by binding each policy to its corresponding configuration section.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRateLimiter(options =>
{
options.AddNCacheConcurrencyLimiter(
"concurrencyPolicy",
builder.Configuration.GetSection("RateLimiting:Concurrency"));
options.AddNCacheFixedWindowLimiter(
"fixedWindowPolicy",
builder.Configuration.GetSection("RateLimiting:FixedWindow"));
options.AddNCacheTokenBucketLimiter(
"tokenBucketPolicy",
builder.Configuration.GetSection("RateLimiting:TokenBucket"));
options.AddNCacheSlidingWindowLimiter(
"slidingWindowPolicy",
builder.Configuration.GetSection("RateLimiting:SlidingWindow"));
});
var app = builder.Build();
app.UseRateLimiter();
app.Run();
If a configuration section is missing or cannot be bound to the required options type, the registration throws an InvalidOperationException.
Configuration Properties
All NCache RateLimiting option classes inherit the following base properties from NCache.OSS.RateLimiting.RateLimiterOptions.
| Property | Description |
|---|---|
CacheName |
Specifies the NCache cache used to store rate limiter state. This value has no default. If it is empty, the cache connection fails when the limiter is first used. |
ServerList |
Specifies an optional list of NCache server nodes. Each entry contains an IP address and port. If this value is not provided, the connection falls back to cache resolution through client.ncconf. |
Port |
Specifies the default server port. The default value is 9800. This value is used for server entries that do not specify their own port. |
ConcurrencyRateLimiterOptions
| Property | Description |
|---|---|
PermitLimit |
Specifies the maximum number of concurrent permits allowed for the policy partition. This value has no default and should be set explicitly. |
QueueLimit |
Specifies the maximum number of queued requests allowed after PermitLimit is reached. This value has no default and should be set explicitly. |
TryDequeuePeriod |
Specifies the polling interval for background dequeue synchronization attempts. The default value is 1 second. |
ExpectedRequestTimeout |
Specifies the maximum expected request execution duration. Expired leases are automatically reclaimed. The default value is 60 seconds. |
LockTimeout |
Specifies the lock timeout used for distributed coordination across cluster nodes. The default value is 10 seconds. |
FixedWindowLimiterOptions
| Property | Description |
|---|---|
PermitLimit |
Specifies the maximum number of permits allowed inside a single fixed window. This value has no default and should be set explicitly. |
Window |
Specifies the fixed window duration before the count resets. The default value is TimeSpan.Zero, which can effectively reject requests until set. |
LockTimeout |
Specifies the lock timeout used for distributed coordination across cluster nodes. The default value is 10 seconds. |
TokenBucketLimiterOptions
| Property | Description |
|---|---|
TokenLimit |
Specifies the maximum number of tokens the bucket can hold at any given time. This value has no default and should be set explicitly. |
TokensPerPeriod |
Specifies the number of tokens added to the bucket at each replenishment tick. This value has no default and should be set explicitly. |
ReplenishmentPeriod |
Specifies the recurring interval at which tokens are replenished. This value has no default and should be set explicitly. |
LockTimeout |
Specifies the lock timeout used for distributed coordination across cluster nodes. The default value is 5 seconds. |
SlidingWindowLimiterOptions
| Property | Description |
|---|---|
PermitLimit |
Specifies the maximum number of permits allowed in any trailing window-length interval. This value has no default and should be set explicitly. |
Window |
Specifies the trailing interval length. The default value is TimeSpan.Zero. |
LockTimeout |
Specifies the lock timeout used for distributed coordination across cluster nodes. The default value is 5 seconds. |
Validation Rules
When any AddNCacheXxxLimiter method is called, the following values are validated at registration time:
optionspolicyNameconfigureOptionsfor Action Delegate overloadsconfigSectionforIConfigurationSectionoverloads
If any of these values are null, the method throws an ArgumentNullException.
For configuration-file-based overloads, the configuration section must bind successfully to the required options type. If binding fails, the method throws an InvalidOperationException.
Note
Limiter values such as PermitLimit, QueueLimit, TokenLimit, and Window are not validated as required at registration time. If these values are left unset or remain at zero, the policy can register successfully but may reject all requests during evaluation.
Best Practices
- Set
PermitLimit,QueueLimit,Window,TokenLimit,TokensPerPeriod, andReplenishmentPeriodexplicitly where applicable. - Ensure every node enforcing a given policy uses the same configuration values.
- Pre-allocate enough
QueueLimitfor concurrency policies to handle distributed request bursts. - Match
WindowandReplenishmentPeriodvalues to downstream system capacity. - Avoid creating a very large number of distinct policies in high-tenant deployments.