Skip to content

Commit

Permalink
releaseb fast updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Sammers21 committed Dec 28, 2023
1 parent d216403 commit cd164bb
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 123 deletions.
2 changes: 1 addition & 1 deletion settings/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<pattern>%date{ISO8601} [%thread] %-5level %logger{40} - %msg %n</pattern>
</encoder>
</appender>
<logger name="io.github.sammers.pla" level="DEBUG"/>
<!-- <logger name="io.github.sammers.pla" level="DEBUG"/> -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
Expand Down
9 changes: 6 additions & 3 deletions src/io/github/sammers/pla/blizzard/BlizzardAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io.github.sammers.pla.Main;
import io.github.sammers.pla.db.Character;
import io.github.sammers.pla.logic.CharacterCache;
import io.github.sammers.pla.logic.WoWAPIRateLimiter;
import io.github.sammers.pla.logic.RateLimiter;
import io.github.sammers.pla.logic.Refs;
import io.reactivex.Completable;
import io.reactivex.Maybe;
Expand All @@ -27,6 +27,7 @@

import static io.github.sammers.pla.logic.Conts.EU;
import static io.github.sammers.pla.logic.Conts.US;
import java.util.concurrent.TimeUnit;

/**
* Blizzard API.
Expand All @@ -44,7 +45,9 @@ public class BlizzardAPI {
private final Map<String, Cutoffs> cutoffs;
private final String clientId;
private final AtomicReference<BlizzardAuthToken> token = new AtomicReference<>();
private final WoWAPIRateLimiter woWAPIRateLimiter = new WoWAPIRateLimiter(Main.VTHREAD_SCHEDULER);
private final RateLimiter rateLimiter = new RateLimiter(100, TimeUnit.SECONDS, 1000,
Optional.of(new RateLimiter(36000, TimeUnit.HOURS, 1000, Optional.empty(), Main.VTHREAD_SCHEDULER)),
Main.VTHREAD_SCHEDULER);

public BlizzardAPI(String clientId, String clientSecret, WebClient webClient, Refs refs, CharacterCache characterCache, Map<String, Cutoffs> cutoffs) {
this.clientId = clientId;
Expand All @@ -56,7 +59,7 @@ public BlizzardAPI(String clientId, String clientSecret, WebClient webClient, Re
}

public Completable rpsToken() {
return woWAPIRateLimiter.request();
return rateLimiter.request();
}

public Single<BlizzardAuthToken> token() {
Expand Down
54 changes: 52 additions & 2 deletions src/io/github/sammers/pla/logic/RateLimiter.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,64 @@ public class RateLimiter {
private final LinkedList<Long> secondRing = new LinkedList<>();
private final ConcurrentLinkedQueue<CompletableEmitter> requestQes = new ConcurrentLinkedQueue<>();
private final int maxRequestsTotal;
private final Optional<RateLimiter> parent;

public RateLimiter(int permits, TimeUnit per, int maxRequestsTotal, Optional<RateLimiter> parent, Scheduler scheduler) {

this.maxRequestsTotal = maxRequestsTotal;
this.parent = parent;
long now = System.currentTimeMillis();
long duration = per.toMillis(permits);
long step = duration / permits;
for (int i = 0; i < permits; i++) {
secondRing.add(now - (i * step));
}
scheduler.scheduleDirect(() -> {
while (true) {
CompletableEmitter src = requestQes.poll();
while (src != null) {
if (secondRing.size() < permits) {
secondRing.add(System.currentTimeMillis());
src.onComplete();
} else {
Long oldestSecondR = secondRing.poll();
if (oldestSecondR != null) {
Long sleepSecondRing = 1000 - (System.currentTimeMillis() - oldestSecondR);
if (sleepSecondRing > 0) {
try {
log.debug("Sleeping for {} ms", sleepSecondRing);
Thread.sleep(sleepSecondRing);
} catch (InterruptedException e) {
log.error("Interrupted", e);
}
}
}
secondRing.add(System.currentTimeMillis());
src.onComplete();
}
src = requestQes.poll();
}
try {
log.trace("Sleeping for {} ms after processing all requests", 100);
Thread.sleep(100);
} catch (InterruptedException e) {
log.error("Interrupted", e);
}
}
});
}

public Completable request() {
if (parent.isPresent()) {
return parent.get().request().andThen(request0());
} else {
return request0();
}
}

private Completable request0() {
if (requestQes.size() > maxRequestsTotal) {
return Completable.error(new IllegalStateException("There are too many requests in the queue. Current size: " + requestQes.size()));
return Completable.error(new IllegalStateException(
"There are too many requests in the queue. Current size: " + requestQes.size()));
} else {
return Completable.create(requestQes::add);
}
Expand Down
83 changes: 0 additions & 83 deletions src/io/github/sammers/pla/logic/WoWAPIRateLimiter.java

This file was deleted.

81 changes: 81 additions & 0 deletions test/io/github/sammers/pla/logic/RateLimiterTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package io.github.sammers.pla.logic;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import io.reactivex.Completable;
import io.reactivex.Scheduler;
import io.vertx.core.cli.Option;

import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.Optional;
import java.util.ArrayList;

public class RateLimiterTest {

public static final Scheduler VTHREAD_EXECUTOR = io.reactivex.schedulers.Schedulers.from(Executors.newVirtualThreadPerTaskExecutor());

@Test
public void basic() {
Long start = System.currentTimeMillis();
RateLimiter woWAPIRateLimiter = new RateLimiter(2, TimeUnit.SECONDS, 100, Optional.empty(), VTHREAD_EXECUTOR);
Completable.merge(
List.of(
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request()
)
).blockingAwait();
assertTimePassed(start, 5000L);
}

@Test
public void basicWithHr() {
Long start = System.currentTimeMillis();
RateLimiter woWAPIRateLimiter = new RateLimiter(36000, TimeUnit.HOURS, 100, Optional.empty(), VTHREAD_EXECUTOR);
Completable.merge(
List.of(
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request(),
woWAPIRateLimiter.request()
)
).blockingAwait();
assertTimePassed(start, 1000L);
}

@Test
public void complexBlizzardLike() {
Long start = System.currentTimeMillis();
RateLimiter woWAPIRateLimiter = new RateLimiter(10, TimeUnit.SECONDS, 100,
Optional.of(new RateLimiter(10, TimeUnit.SECONDS, 100, Optional.empty(), VTHREAD_EXECUTOR)),
VTHREAD_EXECUTOR);
List<Completable> x = new ArrayList<>();
for (int i = 0; i < 10; i++) {
x.add(woWAPIRateLimiter.request());
}
Completable.merge(x).blockingAwait();
assertTimePassed(start, 1000L);
}


private void assertTimePassed(Long start, Long passed) {
String exp = "Expected " + passed + " ms to pass, but only " + (System.currentTimeMillis() - start) + " ms passed";
boolean b = (System.currentTimeMillis() - start) >= passed;
Assertions.assertTrue(b, exp);
}
}
34 changes: 0 additions & 34 deletions test/io/github/sammers/pla/logic/WoWAPIRateLimiterTest.java

This file was deleted.

0 comments on commit cd164bb

Please sign in to comment.