-
Notifications
You must be signed in to change notification settings - Fork 1.7k
#585 #1229 #1598 #1915 Rate limiting global configuration #2294
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
base: develop
Are you sure you want to change the base?
#585 #1229 #1598 #1915 Rate limiting global configuration #2294
Conversation
Hello, Milad! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yellow card penalty! 🟨
Thank once again for submitting this PR However, after the code review, I consider it a draft.
The primary concern is the lack both unit and acceptance tests ❗
While your intention to add documentation is appreciated, please note that documentation should be written upon the completion of the code (at the end of development).
Several minor but important issues need to be addressed as listed below 👇
- I recommend starting with the file models design.
- Please be aware that the linked #1229 issue requires grouping routes by a key. Prioritize this type of grouping initially, and upon completion, proceed with the Patterns concept.
- Presenting your ideas using patterns and method grouping is also necessary. However, I do not support your idea of including the "Methods" feature, as it mixes concerns.
P.S. I will provide my vision of the model's design at a later stage.
P.P.S. I anticipate several rounds of code review. However, this feat has high priority, and I will support you through multiple pair-programming sessions.
P.P.P.S. Other members from the Ocelot development team could review this PR and provide separate code feedback 😉
Life, indeed, not easy 😛
|
||
.. code-block:: json | ||
|
||
"GlobalRateLimitRules": [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To which section does it belong?
It would be preferable to display hierarchy within the GlobalConfiguration
section.
* - ``Pattern`` | ||
- The downstream path template pattern to match (using Ocelot’s syntax). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is grouping based on path patterns your idea? 🤔
It seems this approach was not discussed in #1229, where @neetra requested Ocelot’s standard method of grouping based on keys, aka the Key property of a route.
Therefore, grouping by a pattern appears to be a more advanced approach. That said, I have no objections to introducing this feature, provided that grouping by a key is implemented as well.
@@ -42,6 +43,7 @@ public FileGlobalConfiguration() | |||
public FileMetadataOptions MetadataOptions { get; set; } | |||
public FileQoSOptions QoSOptions { get; set; } | |||
public FileRateLimitOptions RateLimitOptions { get; set; } | |||
public IEnumerable<FileGlobalRateLimit> GlobalRateLimitRules { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
The property is defined to return
IEnumerable<FileGlobalRateLimit>
, but in the constructor, you assign it asList<FileGlobalRateLimit>
❗ This approach is suboptimal for performance. Please note that File* models contain deeply encapsulated data and properties. Thus, it is advisable define a more specific type, with options beingList<T>
andDictionary<TK, TV>
. -
Another concern arises. Why is the "Global" prefix used in the name of a property that is already part of
GlobalConfiguration
? Kindly rename the property by omitting the "Global" prefix.
|
||
namespace Ocelot.Configuration.Creator | ||
{ | ||
public sealed class GlobalRateLimitOptionsCreator : IGlobalRateLimitOptionsCreator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The creator's logic heavily relies on the chosen models. Therefore, I am postponing this class review until we reach an agreement on the model design.
private readonly IVersionPolicyCreator _versionPolicyCreator; | ||
private readonly IMetadataCreator _metadataCreator; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No actual changes, but see a GitHub diff. Why? Line endings?
Avoid altering line endings (EOL) if you are not making any changes to the line.
Ensure that you understand the EOL Gotchas in the development process.
{ | ||
public string Name { get; init; } | ||
public string Pattern { get; init; } | ||
public List<string> Methods { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pardon me? Why methods?
Are you genuinely suggesting implementing rate limiting based on a method? Astonishing!
public sealed record FileGlobalRateLimit | ||
{ | ||
public string Name { get; init; } | ||
public string Pattern { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You propose to use Pattern
property for grouping routes to apply the rule. 👌
I suggest defining this property within the FileRateLimitOptions
. However, the final design of the models requires further discussion.
{ | ||
public string Name { get; set; } | ||
public Regex Pattern { get; init; } // wildcard → regex | ||
public HashSet<string> Methods { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Methods
property appears to be a new feature ❕
Please remove all code associated with methods until the idea is implemented using patterns.
First, fully develop the feature and deliver it to the develop branch with complete code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No changes detected! This file was mistakenly included in a commit. I will assist you in removing files with such fictitious changes caused by end-of-line (EOL).
@@ -355,6 +355,32 @@ | |||
], | |||
|
|||
"GlobalConfiguration": { | |||
"RequestIdKey": "ot-traceid" | |||
"RequestIdKey": "ot-traceid", | |||
"GlobalRateLimitRules": [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conducting manual tests a valuable practice during development. However, Ocelot team doesn't accept pull requests without accompanying acceptance tests. Therefore, it is essential to create comprehensive tests that demonstrate the proper functionality of your feature.
P.S. Acceptance tests should be added to the RateLimiting folder.
@raman-m Looking at the code, please don't merge |
@ggnaegi LoL 😆 Don't worry! In my opinion, this PR is of poor quality, so I don’t think it will be merged until June. I've requested changes, but I have no idea when the author will start fixing them. Yes, please review 🙏 FYI, the author hasn't implemented grouping by a key as we do in the multiplexer. However, issue #1229 requires this, yet the author ignored the requirements and implemented their own ideas. The main issue with this PR is the design of the file models. Another concern is the inclusion of multiple proposed features. I don’t want to introduce models that will be either useless or difficult to maintain. |
@raman-m Hi Raman, |
I am pleased to welcome your return to development 👍 OK We have this documentation → Global Configuration
"GlobalConfiguration": {
// This is the old section for dynamic routing with service discovery
"RateLimitOptions": {
"ClientIdHeader": "MyRateLimiting",
"DisableRateLimitHeaders": false,
"HttpStatusCode": 418, // I'm a teapot
"QuotaExceededMessage": "Customize Tips!",
"RateLimitCounterPrefix": "ocelot"
},
// I propose the following section ->
"RateLimiting": {
"ByHeader": {},
"ByMethod": {},
"ByIP": {},
"ByAspNet": {},
// and more...
// Common props should go here ->
"StatusCode": 418,
"Message": "Customize Tips!",
"CounterPrefix": "rate-limiting",
// Also I propose to add separate metadata section
"Metadata": {}
}
} Please share your thoughts regarding the new design of the section. |
DisableRateLimitHeaders = g.DisableRateLimitHeaders, | ||
EnableRateLimiting = g.EnableRateLimiting, | ||
}) | ||
.ToList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.ToList(); | |
.ToArray(); |
@@ -42,6 +43,7 @@ public FileGlobalConfiguration() | |||
public FileMetadataOptions MetadataOptions { get; set; } | |||
public FileQoSOptions QoSOptions { get; set; } | |||
public FileRateLimitOptions RateLimitOptions { get; set; } | |||
public IEnumerable<FileGlobalRateLimit> GlobalRateLimitRules { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the property is init-only, so it's presumed immutable, and in this case, I think an IReadOnlyCollection would be more appropriate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct! However, in such cases of collection, we use IList
, IDictionary
or Array
objects.
public List<string> Methods { get; init; } | ||
public int Limit { get; init; } | ||
public string Period { get; init; } | ||
public int HttpStatusCode { get; init; }= 429; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public int HttpStatusCode { get; init; }= 429; | |
public int HttpStatusCode { get; init; }= HttpStatusCode.TooManyRequests; |
{ | ||
public string Name { get; set; } | ||
public Regex Pattern { get; init; } // wildcard → regex | ||
public HashSet<string> Methods { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public HashSet<string> Methods { get; init; } | |
public IReadOnlySet<string> Methods { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies! Is it truly necessary to hash sets in this context? believe a straightforward collection, such as a List
, could suffice.
This property is initialized by the framework's configuration provider, either JSON data or the internal storage of the provider. The specific collection type that will be assigned here remains unknown: it is needed to debug in run-time.
Another point, it seems we must remove Methods
entirely. This is a mixing concerns issue I mentioned in my review feedback. The author did not implement the requirement from the linked issue and began implementing their own ideas. I believe this approach leads to coding inefficiency. Therefore, we must remove all code related to Methods
and reintroduce it in upcoming PRs as a new feature.
Milad, when you mention "ASAP," could you provide us with a timeline for your feedback or commits? |
|
||
.. code-block:: json | ||
|
||
"GlobalRateLimitRules": [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understood the feature correctly, this is a rate limiter that only applies if a route does not have an explicit rate limiter configured. In that case would it make sense to have "fallback" in the name? Maybe FallbackRateLimitingRules?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The property names are excessively long, necessitating a larger JSON file!
FYI, I will be handling this PR because it seems the author doesn't care about their piece of coding art. My proposal for the JSON schema in the global section is the RateLimiting
subsection which will describe multiple types of limiters.
} | ||
} | ||
|
||
public interface IGlobalRateLimitOptionsCreator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend to keep interfaces and implementations separate: each in its own file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough! The interface will be moved to a separate file.
|
||
if (globalRateLimit == null || RateLimitOptions.EnableRateLimiting) | ||
{ | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how I feel about return in ctor 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have pointed out the same problem of implementing business logic inside the constructor... However, the author continues to soar within the realm of his dreams... 😄
I will address this design issue when I begin working on this pull request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closes #585 #1229 #1915
Proposed Changes
This PR implements global rate limiting per #1229.
GlobalRateLimitOptions
in ConfigurationFileGlobalConfiguration
in File