-
Notifications
You must be signed in to change notification settings - Fork 161
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
Refactor RateLimiter #534
Conversation
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 { |
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 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.
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.
It's just because this class is now used for assertions on two distinct classes that have no common interface.
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 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.
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.
Alternatively, we could delete the class since it doesn't do much.
In fact that's probably the best option 👍
@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; | ||
} | ||
|
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 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.
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 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<>(); |
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 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.
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.
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.
a45f5d6
to
5629b7d
Compare
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 newTokenBucketFactory
bean.TokenBucketRateLimiter
should NOT implementRateLimiter
, because that would result in two available beans for this interface: the one selected by configuration (realm-token-bucket
orno-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:
realm-token-bucket
rate limiter were moved from therateLimiter
section to thetokenBucketFactory
section.