Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nimanikoo/Dotnet-RateLimiter/llms.txt

Use this file to discover all available pages before exploring further.

RedisRateLimiter is the heart of the distributed rate limiting system. It wraps a single atomic Lua script that runs inside Redis, which means the counter increment and the limit check happen in one round-trip with no race conditions — even across multiple application instances or pods. Every inbound HTTP request that carries a [RedisRateLimit] attribute is evaluated by this service before it reaches your controller logic.

Namespace

namespace Dotnet_RateLimiter.Services;

Class Declaration

public class RedisRateLimiter
The class is non-sealed and its primary method is marked virtual, making it straightforward to mock or subclass in unit and integration tests without requiring any additional abstraction layer.

Constructor

public RedisRateLimiter(IConnectionMultiplexer connectionMultiplexer)
Resolves the default Redis database from the provided IConnectionMultiplexer and stores it as the private IDatabase _db field used by all subsequent calls.
connectionMultiplexer
IConnectionMultiplexer
required
The StackExchange.Redis multiplexer that manages the connection pool to your Redis server. Registered as a singleton by AddCustomRateLimiter and injected automatically by the ASP.NET Core DI container.

Method: IsAllowedAsync

public virtual async Task<bool> IsAllowedAsync(string key, int maxRequests, TimeSpan window)
Evaluates a rate limit counter in Redis using an atomic Lua script. On each invocation the counter for the given key is incremented by one. If this is the very first increment the key is also given a TTL equal to the window duration, so the counter resets automatically after the window elapses. The method then compares the new counter value against maxRequests and returns true when the request is within quota or false when the limit has been exceeded.
key
string
required
The composite rate-limit identifier, typically constructed by the middleware as {userType}:{identityKey}:{path} (e.g. user:42:/api/weather or guest:192.168.1.1:/api/weather). The method internally prepends rate_limit: so the final Redis key becomes rate_limit:{key}.
maxRequests
int
required
The maximum number of requests permitted within a single window. Sourced directly from RedisRateLimitAttribute.MaxRequests on the matched endpoint.
window
TimeSpan
required
The duration of the rate-limit window. Converted to whole seconds before being passed to the Lua script as the Redis EXPIRE value. Sourced from RedisRateLimitAttribute.WindowSeconds.
returns
Task<bool>
true if the request is within the allowed quota; false if the rate limit has been exceeded for this key in the current window.

Full Implementation

public virtual async Task<bool> IsAllowedAsync(string key, int maxRequests, TimeSpan window)
{
    var script = LuaScript.Prepare(@"
            local current = redis.call('INCR', @key)
            if tonumber(current) == 1 then
                redis.call('EXPIRE', @key, @window)
            end
            if tonumber(current) > tonumber(@maxRequests) then
                return 0
            else
                return 1
            end
        ");

    var result = await _db.ScriptEvaluateAsync(script, new
    {
        key = (RedisKey)$"rate_limit:{key}",
        window = (int)window.TotalSeconds,
        maxRequests = maxRequests
    });

    return (int)result == 1;
}
The Lua script sets the TTL only when the counter reaches 1 (i.e. the very first request in a new window). This ensures the window starts at the moment of the first request and does not slide on every subsequent call — this is a fixed window semantic at the Redis level.

Redis Key Format

SegmentExample value
Prefixrate_limit:
User typeuser / guest
IdentityUser ID (authenticated) or remote IP (anonymous)
Path/api/weatherforecast
Full keyrate_limit:user:42:/api/weatherforecast

Service Registration

RedisRateLimiter is registered as a singleton by AddCustomRateLimiter:
services.AddSingleton<RedisRateLimiter>();
See RateLimiterExtensions for the full registration helper.

Direct Usage Example

While RedisRateLimiter is normally invoked automatically by RedisRateLimitingMiddleware, you can also call it directly — for example inside a background service or a custom policy:
public class MyService
{
    private readonly RedisRateLimiter _rateLimiter;

    public MyService(RedisRateLimiter rateLimiter)
    {
        _rateLimiter = rateLimiter;
    }

    public async Task<bool> CheckLimitAsync(string userId, string resource)
    {
        var key = $"user:{userId}:{resource}";
        var allowed = await _rateLimiter.IsAllowedAsync(
            key,
            maxRequests: 100,
            window: TimeSpan.FromMinutes(1)
        );

        return allowed;
    }
}

Build docs developers (and LLMs) love