Skip to content
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

Refactor RateLimiter #534

Merged
merged 3 commits into from
Dec 17, 2024
Merged

Refactor RateLimiter #534

merged 3 commits into from
Dec 17, 2024

Conversation

adutra
Copy link
Contributor

@adutra adutra commented Dec 11, 2024

This PR is motivated by the following issues:

  • RateLimiter implementations are typically request-scoped, because they are sensitive to the realm context, but keeping a cache of token buckets per realm must be delegated to a separate, application-scoped bean: this is solved by introducing a new TokenBucketFactory bean.
  • TokenBucketRateLimiter should NOT implement RateLimiter, because that would result in two available beans for this interface: the one selected by configuration (realm-token-bucket or no-op), and this one which is always available, thus triggering an unresolvable bean error when using the Quarkus runtime. Instead, this class is now just a general-purpose Token Bucket implementation.

This PR has one minor user-facing change:

  • The configuration options for the realm-token-bucket rate limiter were moved from the rateLimiter section to the tokenBucketFactory section.

import org.junit.jupiter.api.Assertions;

/** Utility class for testing rate limiters. Lets you easily assert the result of tryAcquire(). */
public class RateLimitResultAsserter {
private final RateLimiter rateLimiter;
public class TokenBucketResultAsserter {
Copy link

@sfc-gh-aguterman sfc-gh-aguterman Dec 11, 2024

Choose a reason for hiding this comment

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

I don't think it makes sense to refactor this class specifically. In isolation, making repeated assertions on a generic BooleanSupplier expecting it to return a sequence of trues then false is odd. The expectation that the boolean will change is coupled to the fact that the BooleanSupplier is a RateLimiter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's just because this class is now used for assertions on two distinct classes that have no common interface.

Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if renaming it to something more generic would make it less confusing, as right now it's a middle-ground between a RateLimiterResultAsserter and a BooleanSupplierAsserter. We could rename the class & methods to:

  • BooleanSupplierAsserter
  • expectTrue
  • expectFalse

Alternatively, we could delete the class since it doesn't do much.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alternatively, we could delete the class since it doesn't do much.

In fact that's probably the best option 👍

Comment on lines +341 to +350
@JsonProperty("tokenBucketFactory")
private TokenBucketFactory getTokenBucketFactory() {
return tokenBucketFactory;
}

@JsonProperty("tokenBucketFactory")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
public void setTokenBucketFactory(@Nullable TokenBucketFactory tokenBucketFactory) {
this.tokenBucketFactory = tokenBucketFactory;
}

Choose a reason for hiding this comment

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

Is there a way to scope this config down to the RealmTokenBucketRateLimiter itself? Otherwise the tokenBucketFactory can be unused if you're using a different limiter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried but I'm afraid that it won't be achievable with Dropwizard configuration system + HK2 dependency injection.

BTW we already have the same situation: DefaultPolarisAuthenticator requires a TokenBrokerFactory, but
TestInlineBearerTokenPolarisAuthenticator doesn't – if you use the latter, the tokenBroker configuration section would be ignored.

private final long requestsPerSecond;
private final long windowSeconds;
private final Clock clock;
private final Map<String, TokenBucket> perRealmBuckets = new ConcurrentHashMap<>();
Copy link
Member

Choose a reason for hiding this comment

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

Not new here (so not blocking this PR), but this can be abused to cause an OOM in Polaris if an attacker issues requests with random realm-IDs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This PR is motivated by the following issues:

* `RateLimiter` implementations are typically request-scoped, because they are sensitive to the realm context,
  but keeping a cache of token buckets per realm must be delegated to a separate, application-scoped bean: this
  is solved by introducing a new `TokenBucketFactory` bean.
* `TokenBucketRateLimiter` should NOT implement `RateLimiter`, because that would result in two available beans
  for this interface: the one selected by configuration (`realm-token-bucket` or `no-op`), and this one which
  is always available, thus triggering an unresolvable bean error when using the Quarkus runtime. Instead,
  this class is now just a general-purpose Token Bucket implementation.

This PR has one user-facing change:

* The configuration options for the `realm-token-bucket` rate limiter were moved from the `rateLimiter` section to
  the `tokenBucketFactory` section.
@adutra adutra force-pushed the token-bucket-refactor branch from a45f5d6 to 5629b7d Compare December 14, 2024 18:02
@adutra adutra merged commit 1071aa2 into apache:main Dec 17, 2024
5 checks passed
@adutra adutra deleted the token-bucket-refactor branch December 17, 2024 09:56
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