-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Closed
Labels
api-suggestionEarly API idea and discussion, it is NOT ready for implementationEarly API idea and discussion, it is NOT ready for implementationarea-middlewareIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewaresIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewaresfeature-rate-limitWork related to use of rate limit primitivesWork related to use of rate limit primitives
Description
Background and Motivation
I want to save API rate limits in the database and have the possibility of changing them at runtime (eg: based on the customer's subscription/licensing/etc).
The problem is that IRateLimiterPolicy
doesn't have an async method
public class ClientIdRateLimiterPolicy : IRateLimiterPolicy<string>
{
private readonly IServiceProvider _serviceProvider;
public ClientIdRateLimiterPolicy(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public RateLimitPartition<string> GetPartition(HttpContext httpContext)
{
var clientId = httpContext.Request.Headers["X-ClientId"].ToString();
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<SampleDbContext>();
var rateLimit = dbContext.Clients.Where(x => x.Identifier == clientId).Select(x => x.RateLimit).FirstOrDefault();
return RateLimitPartition.GetConcurrencyRateLimiter(clientId, key => new ConcurrencyRateLimiterOptions
{
PermitLimit = rateLimit?.PermitLimit ?? 1
});
}
}
Proposed API
public interface IRateLimiterPolicy<TPartitionKey>
{
Func<OnRejectedContext, CancellationToken, ValueTask>? OnRejected { get; }
- RateLimitPartition<TPartitionKey> GetPartition(HttpContext httpContext);
+ ValueTask<RateLimitPartition<TPartitionKey>> GetPartitionAsync(HttpContext httpContext);
}
Usage Examples
public class ClientIdRateLimiterPolicy : IRateLimiterPolicy<string>
{
private readonly IServiceProvider _serviceProvider;
public ClientIdRateLimiterPolicy(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async ValueTask<RateLimitPartition<string>> GetPartitionAsync(HttpContext httpContext)
{
var clientId = httpContext.Request.Headers["X-ClientId"].ToString();
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<SampleDbContext>();
var rateLimit = await redisCache.GetAsync(clientId);
var rateLimit = await dbContext.Clients.Where(x => x.Identifier == clientId).Select(x => x.RateLimit).FirstOrDefaultAsync();
return RateLimitPartition.GetConcurrencyRateLimiter(clientId, key => new ConcurrencyRateLimiterOptions
{
PermitLimit = rateLimit?.PermitLimit ?? 1
});
}
}
Risks
Find a way w/o breaking changes
halter73
Metadata
Metadata
Assignees
Labels
api-suggestionEarly API idea and discussion, it is NOT ready for implementationEarly API idea and discussion, it is NOT ready for implementationarea-middlewareIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewaresIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewaresfeature-rate-limitWork related to use of rate limit primitivesWork related to use of rate limit primitives