Skip to content

Add chained rate limiter example #2344

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/strategies/rate-limiter.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,46 @@ new ResiliencePipelineBuilder()
});
```
<!-- endSnippet -->

It is also possible to create a rate limiter strategy using [multiple chained rate limiters](https://learn.microsoft.com/aspnet/core/performance/rate-limit#create-chained-limiters)
to combine multiple rate-limiters with different configurations as shown below.

For example, we can configure a rate limit of 100 requests per minute that also includes a rate limit of 10 requests per second.
The rate limit is partitioned by a key which could, for example, be the user ID associated with an authenticated HTTP request to
apply the rate limits to each user of the system individually.

<!-- snippet: rate-limiter-chained -->
```cs
// Use the user's ID as the partition key.
var partitionKey = "user-id";

var firstSlidingWindow = PartitionedRateLimiter.Create<ResilienceContext, string>((context) =>
{
return RateLimitPartition.GetSlidingWindowLimiter(partitionKey, (partitionKey) => new()
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1),
});
});

var secondSlidingWindow = PartitionedRateLimiter.Create<ResilienceContext, string>((context) =>
{
return RateLimitPartition.GetSlidingWindowLimiter(partitionKey, (partitionKey) => new()
{
PermitLimit = 10,
Window = TimeSpan.FromSeconds(1),
});
});

// Create a rate limiter that combines the two sliding windows.
var chainedRateLimiter = PartitionedRateLimiter.CreateChained(firstSlidingWindow, secondSlidingWindow);

// Create the pipeline using the rate limiter that chains the windows together.
new ResiliencePipelineBuilder()
.AddRateLimiter(new RateLimiterStrategyOptions
{
RateLimiter = (context) => chainedRateLimiter.AcquireAsync(context.Context),
})
.Build();
```
<!-- endSnippet -->
36 changes: 36 additions & 0 deletions src/Snippets/Docs/RateLimiter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,42 @@ public static void Usage()
});

#endregion

#region rate-limiter-chained

// Use the user's ID as the partition key.
var partitionKey = "user-id";

var firstSlidingWindow = PartitionedRateLimiter.Create<ResilienceContext, string>((context) =>
{
return RateLimitPartition.GetSlidingWindowLimiter(partitionKey, (partitionKey) => new()
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1),
});
});

var secondSlidingWindow = PartitionedRateLimiter.Create<ResilienceContext, string>((context) =>
{
return RateLimitPartition.GetSlidingWindowLimiter(partitionKey, (partitionKey) => new()
{
PermitLimit = 10,
Window = TimeSpan.FromSeconds(1),
});
});

// Create a rate limiter that combines the two sliding windows.
var chainedRateLimiter = PartitionedRateLimiter.CreateChained(firstSlidingWindow, secondSlidingWindow);

// Create the pipeline using the rate limiter that chains the windows together.
new ResiliencePipelineBuilder()
.AddRateLimiter(new RateLimiterStrategyOptions
{
RateLimiter = (context) => chainedRateLimiter.AcquireAsync(context.Context),
})
.Build();

#endregion
}

public static async Task HandleRejection()
Expand Down