Skip to content

feat: add per-project rate limiter settings override #583

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 16 commits into
base: main
Choose a base branch
from

Conversation

zmoog
Copy link

@zmoog zmoog commented May 29, 2025

Add a per-project override mechanism for rate limiter settings.

Changes:

  • Add a new ratelimit.overrides option
  • Add a new ratelimit.throttle_interval option

For example, in the following config:

ratelimit/overrides_throttle_interval:
  rate: 100
  burst: 200
  strategy: bytes
  throttle_behavior: error 
  throttle_interval: 10s # ✨New option
  overrides: # ✨New option
    x-elastic-project-id:e678ebd7-3a15-43dd-a95c-1cf0639a6292:
      rate: 400
      burst: 800
      throttle_interval: 42s 

In overrides, you can override one or more of the following settings:

  • rate
  • burst
  • throttle_interval

If not overridden, the base settings apply.

Examples

(1) Simple rate limiter settings (no override)

ratelimit:
  metadata_keys:
   - x-elastic-project-id
  rate: 2046820
  burst: 5117050 # In these examples, I'm considering a 2.5x burst ratio 
  strategy: bytes
  throttle_behavior: error  

Notes on rate and strategy:

Strategy rate value
requests Requests per second
records Records per second
bytes Bytes per seconds

(2) Per-project rate override

ratelimit:
  metadata_keys:
   - x-elastic-project-id
  rate: 2046820
  burst: 5117050 # In these examples, I'm considering a 2.5x burst ratio 
  strategy: bytes
  throttle_behavior: error
  overrides:  
    x-elastic-project-id:local:
      rate: 3070230 # +50% over baseline # 👀

(3) Per-project rate and burst override

ratelimit:
  metadata_keys:
   - x-elastic-project-id
  rate: 2046820
  burst: 5117050 # In these examples, I'm considering a 2.5x burst ratio 
  strategy: bytes
  throttle_behavior: error
  overrides:  
    x-elastic-project-id:local:
      rate: 3070230 # +50% over baseline # 👀
    x-elastic-project-id:whatever:
      rate: 3070230 # +50% over baseline # 👀
      burst: 7675575 # +50% over baseline # 👀

(4) Set base throttle_interval

ratelimit:
  metadata_keys:
   - x-elastic-project-id
  rate: 2046820
  burst: 5117050 # In these examples, I'm considering a 2.5x burst ratio 
  strategy: bytes
  throttle_behavior: error
  throttle_interval: 10s # 👀
  overrides:  
    x-elastic-project-id:local:
      rate: 3070230 # +50% over baseline

(5) Set base throttle_interval to 10s and override it on a project

ratelimit:
  metadata_keys:
   - x-elastic-project-id
  rate: 2046820
  burst: 5117050 # In these examples, I'm considering a 2.5x burst ratio 
  strategy: bytes
  throttle_behavior: error
  throttle_interval: 10s # 👀
  overrides:  
    x-elastic-project-id:local:
      rate: 3070230 # +50% over baseline
    x-elastic-project-id:whatever:
      throttle_interval: 15s # 👀

@zmoog
Copy link
Author

zmoog commented Jun 2, 2025

@simitt, I'd like to double-check with you whether this is the override semantics we're looking for.

Note: I also discussed the switch from a request-based to a bytes-based 1 rate limiting with @vigneshshanmugam. So, in the following examples, I will use the bytes strategy to reflect this.

Let's start with a few examples.

(1) Baseline rate-limiting

In this example, the rate limiter applies the same baseline limits to all projects.

ratelimit:
  metadata_keys:
   - x-elastic-project-id
  rate: 2046820
  burst: 5117050 # In these examples, I'm considering a 2.5x burst ratio 
  strategy: bytes
  throttle_behavior: error  

(2) Per-project rate override

In this example, we increase the rate by 50% over the baseline.

ratelimit:
  metadata_keys:
   - x-elastic-project-id
  rate: 2046820
  burst: 5117050 # In these examples, I'm considering a 2.5x burst ratio 
  strategy: bytes
  throttle_behavior: error
  overrides:  
    x-elastic-project-id:local:
      rate: 3070230 # +50% over baseline

(3) Per-project rate and burst override

In this example, we increase the rate and burst by 50% over the baseline.

ratelimit:
  metadata_keys:
   - x-elastic-project-id
  rate: 2046820
  burst: 5117050 # In these examples, I'm considering a 2.5x burst ratio 
  strategy: bytes
  throttle_behavior: error
  overrides:  
    x-elastic-project-id:local:
      rate: 3070230 # +50% over baseline
    x-elastic-project-id:whatever:
      rate: 3070230 # +50% over baseline
      burst: 7675575 # +50% over baseline

Footnotes

  1. I noticed the comment in the bytes strategy that highlights that the using this strategy is "much more expensive" than that the requests and records strategies. We should consider this aspect, and run some tests to measure this increase — if we don't already have some.

@simitt
Copy link

simitt commented Jun 2, 2025

General approach looks good to me. What would be worth adding is a time interval indicator, e.g. rate_per_second if the rate is applied on a per second interval.
Gubernator also allows to configure the throttling interval. I don't think this is currently set anywhere, and is not the most urgent, but would be a nice add-on to also allow for it. See an example for this in https://github.com/elastic/serverless-gitops/blob/main/services/apm-ingest-service/values/production/default.yaml.

@zmoog zmoog force-pushed the zmoog/feat/allow-per-project-overrides branch from 115e243 to 6395814 Compare June 4, 2025 11:08
@zmoog
Copy link
Author

zmoog commented Jun 4, 2025

General approach looks good to me. What would be worth adding is a time interval indicator, e.g. rate_per_second if the rate is applied on a per second interval. Gubernator also allows to configure the throttling interval. I don't think this is currently set anywhere, and is not the most urgent, but would be a nice add-on to also allow for it. See an example for this in https://github.com/elastic/serverless-gitops/blob/main/services/apm-ingest-service/values/production/default.yaml.

Adding a throttle_interval seems a low hanging fruit. Adding it!

@zmoog zmoog changed the title Add ratelimiter per-project settings override rate limiter: add per-project settings override Jun 4, 2025
Copy link
Contributor

@constanca-m constanca-m left a comment

Choose a reason for hiding this comment

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

Thank you @zmoog ! It looks great, I couldn't find issues with your approach.

// If no override is found, the default rate limit settings are returned.
func resolveRateLimitSettings(cfg *Config, uniqueKey string) RateLimitSettings {
result := cfg.RateLimitSettings
if len(cfg.Overrides) > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

This condition wouldn't be necessary, right? Just doing if override, ok := cfg.Overrides[uniqueKey]; ok { would be enough.

Copy link
Author

Choose a reason for hiding this comment

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

Not needed. This optimization isn't worth sacrificing readability for. I'm removing it.

var errs []error
if r.Rate != nil {
if *r.Rate <= 0 {
errs = append(errs, fmt.Errorf("rate must be greater than zero"))
Copy link
Contributor

Choose a reason for hiding this comment

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

You know... if we were pushing this upstream, and we ran make lint, it would complain that we should use errors.New instead of fmt.Errorf in this case since we are not using any variables in the message :) You don't have to change it, just something that I noticed.

Copy link
Author

Choose a reason for hiding this comment

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

Oh, thank you Constança! I forgot to run make lint before committing the changes 🤦

@zmoog zmoog force-pushed the zmoog/feat/allow-per-project-overrides branch from 98e1d0b to 520fe46 Compare June 5, 2025 09:43
@zmoog zmoog changed the title rate limiter: add per-project settings override feat: add per-project rate limiter settings override Jun 5, 2025
@zmoog zmoog marked this pull request as ready for review June 5, 2025 13:54
@zmoog zmoog requested a review from a team as a code owner June 5, 2025 13:54
@zmoog zmoog requested review from jackshirazi and edmocosta June 5, 2025 13:54
Copy link
Member

@vigneshshanmugam vigneshshanmugam left a comment

Choose a reason for hiding this comment

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

LGTM overall

@zmoog zmoog force-pushed the zmoog/feat/allow-per-project-overrides branch from 03955c2 to dc9e4d4 Compare June 6, 2025 11:12
Copy link
Member

@vigneshshanmugam vigneshshanmugam left a comment

Choose a reason for hiding this comment

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

LGTM

ratelimiter:
metadata_keys:
- x-elastic-project-id
Copy link
Member

Choose a reason for hiding this comment

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

To keep it generic, lets use x-tenant-id or similar.

| `type` | Type of rate limiter. Options are `local` or `gubernator`. | No | `local` |
| `overrides` | Map of metadata key overrides for the rate limiter. See the possible overrides and examples below. | No | |

### Possible overrides:
Copy link
Member

Choose a reason for hiding this comment

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

nit: Overrides based on Metadata keys?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants