Open
Conversation
…t model. The rate limiting rules were refactored to utilize semaphores to ensure limit counting is consistent across multiple threads. The rate limiter was updated to use a RequestModel so that requests can be limited based on different request metadata: request path, user id, organization id, IP address, or region. Tests were updated to reflect these changes.
The DataStoreKeyGenerator utilizes the strategy pattern to generate different types of data store keys that a rate limit rule can use to store the rate limit counting data. Doing so allows the RequestPerTimeSpan rule logic to be reused with different types of keys, e.g. RequestsPerResource, RequestsPerUser, etc... In this way, new types of RequestPerTimeSpan rules can be created by simply added a new data store key type.
Added the allowRequestsOnFailure parameter to the rate limiter In order to allow the user of this library to configure whether the rate limiter will allow or deny requests when an exception occurs. This configuration parameter will give us the flexibility to choose whether we allow or block requests in failure scenarios. Although there is currently only one implementation of each data store that stores the data in memory, we may eventually want to utilize a Redis distributed cache. This is one such case where failures outside of our system may cause the rate limiter to fail.
Added tests for factories and store key generator. Added code to append the enum to the NotImplementedException error messsages. Reorganized using statements.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
In the following sections, I will do my best to explain my design process as well as how to use the Rate Limiter system.
Request Model
The
RequestModelencapsulates all of the different data that can be used to identify and rate limit a given request.Rate Limiter
The
RateLimiteris designed to do the following:RegisterRule- Registers anIRateLimitRuleby adding it to the rate limiter's ruleset data storeIsRequestAllowedAsync- goes through all configured rules for the resource that theRequsetModelrequest is asking to access and checks each rule to ensure limits have not been exceeded.Rate Limiting Rules
You can add a new rate limiting rule through the following steps:
MyNewRuleBaseRateLimitRuleif you wish to utilize the semaphore logic withinIsRequestAllowedAsyncBaseRateLimitRule, then your subclass must implement theProcessRuleAsyncmethodBaseRateLimitRule, then your subclass should implement theIsRequestAllowedAsyncdirectlyConstants.RateLimitRuleTypes.csRateLimitRuleFactory.CreateRulemethodRate Limit Data Store
In order to make it easier to switch from in-memory data store / cache to external data stores/caches, such as
Redis, I chose to do the following:IRateLimitDataStoreinterfaceIRateLimitDataStoreA new rate limit data store can be added to the system by doing the following:
MyNewRateLimitDataStoreIRateLimitDataStoreinterfaceConstants.RateLimitDateStoreTypesenum forMyNewRateLimitDataStoreRateLimitDataStoreFactory.CreateDataStoremethodData Store Key Generator
While developing different rate limit rules, I noticed that the rule logic could be shared across multiple instances. Rather than duplicate that logic, it seemed better to reuse the logic by creating a way to generate different rate limit data store keys based on the request and use those keys to access the data from the data store. To achieve this, I created the
DataStoreKeyGenerator. One of the major benefits to utilizing the strategy pattern and adding aDataStoreKeyGeneratorturned out to be how easy it was to extend rule functionality and support different types of rules more easily.The
DataStoreKeyGeneratorgenerates a data store key string based on theDataStoreKeyTypeand the givenRequestModelrequest. For example, theDataStoreKeyTypes.RequestsPerUserPerResourcecreates a key string by concatenating therequest.UserIdandrequest.RequestPathtogether.To add a new
DataStoreKeyType:Constants.DataStoreKeyTypesStores.DataStoreKeyGeneratorfor the new enum type entrySample Usage
Instantiating the rate limiter:
Creating rules and registering them:
Checking if a request should be allowed: