use of es.moki.ratelimitj.core.limiter.request.RequestLimitRule in project ratelimitj by mokies.
the class InMemorySlidingWindowRequestRateLimiter method eqOrGeLimit.
private boolean eqOrGeLimit(String key, int weight, boolean strictlyGreater) {
requireNonNull(key, "key cannot be null");
requireNonNull(rules, "rules cannot be null");
if (rules.isEmpty()) {
throw new IllegalArgumentException("at least one rule must be provided");
}
final long now = timeSupplier.get();
// TODO implement cleanup
final int longestDurationSeconds = rules.stream().map(RequestLimitRule::getDurationSeconds).reduce(Integer::max).orElse(0);
List<SavedKey> savedKeys = new ArrayList<>(rules.size());
Map<String, Long> keyMap = getMap(key, longestDurationSeconds);
boolean geLimit = false;
// TODO perform each rule calculation in parallel
for (RequestLimitRule rule : rules) {
SavedKey savedKey = new SavedKey(now, rule.getDurationSeconds(), rule.getPrecision());
savedKeys.add(savedKey);
Long oldTs = keyMap.get(savedKey.tsKey);
oldTs = oldTs != null ? oldTs : savedKey.trimBefore;
if (oldTs > now) {
// don't write in the past
return true;
}
// discover what needs to be cleaned up
long decr = 0;
List<String> dele = new ArrayList<>();
long trim = Math.min(savedKey.trimBefore, oldTs + savedKey.blocks);
for (long oldBlock = oldTs; oldBlock == trim - 1; oldBlock++) {
String bkey = savedKey.countKey + oldBlock;
Long bcount = keyMap.get(bkey);
if (bcount != null) {
decr = decr + bcount;
dele.add(bkey);
}
}
// handle cleanup
Long cur;
if (!dele.isEmpty()) {
dele.forEach(keyMap::remove);
final long decrement = decr;
cur = keyMap.compute(savedKey.countKey, (k, v) -> v - decrement);
} else {
cur = keyMap.get(savedKey.countKey);
}
// check our limits
long count = coalesce(cur, 0L) + weight;
if (count > rule.getLimit()) {
// over limit, don't record request
return true;
} else if (!strictlyGreater && count == rule.getLimit()) {
// at limit, do record request
geLimit = true;
}
}
// there is enough resources, update the counts
if (weight != 0) {
for (SavedKey savedKey : savedKeys) {
// update the current timestamp, count, and bucket count
keyMap.put(savedKey.tsKey, savedKey.trimBefore);
Long computedCountKeyValue = keyMap.compute(savedKey.countKey, (k, v) -> coalesce(v, 0L) + weight);
Long computedCountKeyBlockIdValue = keyMap.compute(savedKey.countKey + savedKey.blockId, (k, v) -> coalesce(v, 0L) + weight);
if (LOG.isDebugEnabled()) {
LOG.debug("{} {}={}", key, savedKey.countKey, computedCountKeyValue);
LOG.debug("{} {}={}", key, savedKey.countKey + savedKey.blockId, computedCountKeyBlockIdValue);
}
}
}
return geLimit;
}
use of es.moki.ratelimitj.core.limiter.request.RequestLimitRule in project ratelimitj by mokies.
the class InMemoryRateLimiterFactoryTest method shouldReturnTheSameInstanceForSameSetOfRules.
@Test
void shouldReturnTheSameInstanceForSameSetOfRules() {
RequestLimitRule rule1a = RequestLimitRule.of(1, TimeUnit.MINUTES, 10);
RequestLimitRule rule1b = RequestLimitRule.of(1, TimeUnit.HOURS, 100);
RequestRateLimiter rateLimiter1 = factory.getInstance(ImmutableSet.of(rule1a, rule1b));
RequestLimitRule rule2a = RequestLimitRule.of(1, TimeUnit.MINUTES, 10);
RequestLimitRule rule2b = RequestLimitRule.of(1, TimeUnit.HOURS, 100);
RequestRateLimiter rateLimiter2 = factory.getInstance(ImmutableSet.of(rule2a, rule2b));
assertThat(rateLimiter1).isSameAs(rateLimiter2);
}
use of es.moki.ratelimitj.core.limiter.request.RequestLimitRule in project ratelimitj by mokies.
the class AbstractAsyncRequestRateLimiterTest method shouldLimitSingleWindowAsync.
@Test
void shouldLimitSingleWindowAsync() throws Exception {
ImmutableSet<RequestLimitRule> rules = ImmutableSet.of(RequestLimitRule.of(10, TimeUnit.SECONDS, 5));
AsyncRequestRateLimiter rateLimiter = getAsyncRateLimiter(rules, timeBandit);
Queue<CompletionStage> stageAsserts = new ConcurrentLinkedQueue<>();
Stream.generate(() -> "ip:127.0.0.2").limit(5).forEach(key -> {
timeBandit.addUnixTimeMilliSeconds(1000L);
stageAsserts.add(rateLimiter.overLimitAsync(key).thenAccept(result -> assertThat(result).isFalse()));
});
for (CompletionStage stage : stageAsserts) {
stage.toCompletableFuture().get();
}
assertThat(rateLimiter.overLimitAsync("ip:127.0.0.2").toCompletableFuture().get()).isTrue();
}
use of es.moki.ratelimitj.core.limiter.request.RequestLimitRule in project ratelimitj by mokies.
the class AbstractReactiveRequestRateLimiterTest method shouldLimitSingleWindowReactive.
@Test
void shouldLimitSingleWindowReactive() {
ImmutableSet<RequestLimitRule> rules = ImmutableSet.of(RequestLimitRule.of(10, TimeUnit.SECONDS, 5));
ReactiveRequestRateLimiter rateLimiter = getRateLimiter(rules, timeBandit);
Flux<Boolean> overLimitFlux = Flux.just("ip:127.0.1.5").repeat(5).flatMap(key -> {
timeBandit.addUnixTimeMilliSeconds(100);
return rateLimiter.overLimitWhenIncrementedReactive(key);
});
overLimitFlux.toStream().forEach(result -> assertThat(result).isFalse());
assertThat(rateLimiter.overLimitWhenIncrementedReactive("ip:127.0.1.5").block()).isTrue();
}
use of es.moki.ratelimitj.core.limiter.request.RequestLimitRule in project ratelimitj by mokies.
the class AbstractReactiveRequestRateLimiterTest method shouldResetLimit.
@Test
void shouldResetLimit() {
ImmutableSet<RequestLimitRule> rules = ImmutableSet.of(RequestLimitRule.of(60, TimeUnit.SECONDS, 1));
ReactiveRequestRateLimiter rateLimiter = getRateLimiter(rules, timeBandit);
String key = "ip:127.1.0.1";
assertThat(rateLimiter.overLimitWhenIncrementedReactive(key).block()).isFalse();
assertThat(rateLimiter.overLimitWhenIncrementedReactive(key).block()).isTrue();
assertThat(rateLimiter.resetLimitReactive(key).block()).isTrue();
assertThat(rateLimiter.resetLimitReactive(key).block()).isFalse();
assertThat(rateLimiter.overLimitWhenIncrementedReactive(key).block()).isFalse();
}
Aggregations