Skip to content

#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

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from

Conversation

MiladRv
Copy link

@MiladRv MiladRv commented May 18, 2025

@coveralls
Copy link
Collaborator

coveralls commented May 19, 2025

Coverage Status

coverage: 85.256% (-0.7%) from 85.965%
when pulling 4dd103f on MiladRv:feature/add-global-rate-limit
into 0b794b3 on ThreeMammals:develop.

@raman-m raman-m added feature A new feature Rate Limiting Ocelot feature: Rate Limiting Configuration Ocelot feature: Configuration labels May 19, 2025
@raman-m raman-m added this to the Spring'25 milestone May 19, 2025
@raman-m
Copy link
Member

raman-m commented May 19, 2025

Hello, Milad!
Thank you for this great PR! I'll be reviewing the code shortly...
However, since Coveralls has reported a decrease in coverage by -0.7%, we need to write unit tests—ideally covering all new code. Unit tests are a crucial part of our development process ❕ Read points 4 and 5.

@raman-m raman-m changed the title Feature: implement configuration to enable RateLimit globaly Rate limiting global configuration May 19, 2025
@raman-m raman-m requested review from RaynaldM, raman-m and ggnaegi May 19, 2025 10:04
@raman-m raman-m changed the title Rate limiting global configuration #585 #1229 #1598 #1915 Rate limiting global configuration May 19, 2025
@raman-m raman-m added the high High priority label May 19, 2025
@raman-m raman-m linked an issue May 19, 2025 that may be closed by this pull request
Copy link
Member

@raman-m raman-m left a 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 👇

  1. I recommend starting with the file models design.
  2. 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.
  3. 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": [
Copy link
Member

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.

Comment on lines +130 to +131
* - ``Pattern``
- The downstream path template pattern to match (using Ocelot’s syntax).
Copy link
Member

@raman-m raman-m May 19, 2025

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; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The property is defined to return IEnumerable<FileGlobalRateLimit>, but in the constructor, you assign it as List<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 being List<T> and Dictionary<TK, TV>.

  2. 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
Copy link
Member

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.

Comment on lines 25 to 26
private readonly IVersionPolicyCreator _versionPolicyCreator;
private readonly IMetadataCreator _metadataCreator;
Copy link
Member

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; }
Copy link
Member

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; }
Copy link
Member

@raman-m raman-m May 19, 2025

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; }
Copy link
Member

@raman-m raman-m May 19, 2025

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.

Copy link
Member

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": [
Copy link
Member

@raman-m raman-m May 19, 2025

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.

@ggnaegi
Copy link
Member

ggnaegi commented May 20, 2025

@raman-m Looking at the code, please don't merge

@raman-m
Copy link
Member

raman-m commented May 20, 2025

@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
Copy link
Member

raman-m commented May 21, 2025

@MiladRv Hi Milad !
When do you plan to begin addressing the code review issues?

@MiladRv
Copy link
Author

MiladRv commented May 23, 2025

@raman-m Hi Raman,
Thanks for your time to review my changes, I'll fix them ASAP.

@raman-m
Copy link
Member

raman-m commented May 23, 2025

I am pleased to welcome your return to development 👍
Based on the feedback from my code review, I suggest concentrating on the design of the models.

OK We have this documentation → Global Configuration
The JSON issue lies in the different types of rate limiting. At present, Ocelot implements rate limiting based on the client's header. In the near future—hopefully within this year—we aim to introduce rate limiting based on the client's IP. I propose separating these types of rate limiting in this pull request, as it is a task that should be prioritized and completed ASAP.

  1. Preserve RateLimitOptions for dynamic routing to maintain backward compatibility, while considering potential improvements. Refer to point 2 for static routing and grouping.
  2. Introduce new RateLimiting section →
"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();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.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; }
Copy link
Collaborator

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

Copy link
Member

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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public HashSet<string> Methods { get; init; }
public IReadOnlySet<string> Methods { get; init; }

Copy link
Member

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.

@raman-m
Copy link
Member

raman-m commented May 27, 2025

@yjorayev Welcome to code review!
Please write a short message here.
My understanding is that this PR should be merged first because we will develop a universal model schema for the RateLimiting JSON section. For ASP.NET rate limiting I propose to configure with ByAspNet subsection.

@raman-m
Copy link
Member

raman-m commented May 27, 2025

@MiladRv wrote on May 23

@raman-m Hi Raman, Thanks for your time to review my changes, I'll fix them ASAP.

Milad, when you mention "ASAP," could you provide us with a timeline for your feedback or commits?
FYI Our Development ProcessBest PracticesRemain online after submitting a pull request
Are you busy?

@raman-m raman-m added the Spring'25 Spring 2025 release label May 27, 2025

.. code-block:: json

"GlobalRateLimitRules": [

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?

Copy link
Member

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

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.

Copy link
Member

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;

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 😆

Copy link
Member

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.

Copy link
Member

@raman-m raman-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Red card penalty 🟥

@MiladRv Milad,
You have received a red card 🟥 penalty because earlier, you got a yellow 🟨 one 2 weeks ago! 😜
Could you kindly begin addressing the review issues? Otherwise, the Ocelot team will take over this PR themselves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Configuration Ocelot feature: Configuration feature A new feature high High priority Rate Limiting Ocelot feature: Rate Limiting Spring'25 Spring 2025 release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Apply rate limiting globally Support Global configuration of load balancer and rate limiting options etc for ReRoutes
6 participants