Skip to content

Commit 7fcd64e

Browse files
authored
Merge pull request #3 from Majored/main
Alter rate limiting approach to be better in multi-threaded contexts
2 parents 3159296 + 33c793c commit 7fcd64e

File tree

2 files changed

+78
-6
lines changed

2 files changed

+78
-6
lines changed

src/main/java/is/swan/mcmarketapi/request/Client.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package is.swan.mcmarketapi.request;
22

33
import is.swan.mcmarketapi.Token;
4+
import is.swan.mcmarketapi.request.Request.Method;
45
import is.swan.mcmarketapi.utils.HTTPUtil;
56

67
public class Client {
78

89
private final Token token;
10+
private final Throttler throttler;
911

1012
public Client(Token token) {
1113
this.token = token;
14+
this.throttler = new Throttler();
1215
}
1316

1417
public Response send(Request request) {
@@ -24,18 +27,33 @@ public Response send(Request request) {
2427
}
2528

2629
public Response sendOrWait(Request request) {
27-
Response response = getResponse(request);
28-
29-
while (response.isRatelimited()) {
30+
long stallFor;
31+
while ((stallFor = this.throttler.stallFor(request.getMethod())) > 0) {
3032
try {
31-
Thread.sleep(response.getMillisecondsToWait());
33+
Thread.sleep(stallFor);
3234
} catch (InterruptedException e) {
3335
e.printStackTrace();
3436
}
35-
36-
response = getResponse(request);
37+
}
38+
39+
Response response = getResponse(request);
40+
41+
if (response.isRatelimited()) {
42+
if (request.getMethod() == Method.GET) {
43+
throttler.setRead(response.getMillisecondsToWait());
44+
} else {
45+
throttler.setWrite(response.getMillisecondsToWait());
46+
}
47+
48+
return sendOrWait(request);
3749
}
3850

51+
if (request.getMethod() == Method.GET) {
52+
throttler.resetRead();
53+
} else {
54+
throttler.resetWrite();
55+
}
56+
3957
if (response.getError() != null) {
4058
return response;
4159
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package is.swan.mcmarketapi.request;
2+
3+
import java.util.concurrent.atomic.AtomicLong;
4+
5+
import is.swan.mcmarketapi.request.Request.Method;
6+
7+
public class Throttler {
8+
private final AtomicLong readLastRetry = new AtomicLong(0);
9+
private final AtomicLong readLastRequest = new AtomicLong(System.currentTimeMillis());
10+
11+
private final AtomicLong writeLastRetry = new AtomicLong(0);
12+
private final AtomicLong writeLastRequest = new AtomicLong(System.currentTimeMillis());
13+
14+
public long stallFor(Method method) {
15+
long time = System.currentTimeMillis();
16+
17+
if (method == Method.GET) {
18+
return Throttler.stalForHelper(this.readLastRetry, this.readLastRequest, time);
19+
} else {
20+
return Throttler.stalForHelper(this.writeLastRetry, this.writeLastRequest, time);
21+
}
22+
}
23+
24+
private static long stalForHelper(AtomicLong aLastRetry, AtomicLong aLastRequest, long time){
25+
long lastRetry = aLastRetry.getAcquire();
26+
long lastRequest = aLastRequest.getAcquire();
27+
28+
if (lastRetry > 0 && (time - lastRequest) < lastRetry) {
29+
return lastRetry - (time - lastRequest);
30+
} else {
31+
return 0;
32+
}
33+
}
34+
35+
public void setRead(long value) {
36+
readLastRetry.setRelease(value);
37+
readLastRequest.setRelease(System.currentTimeMillis());
38+
}
39+
40+
public void setWrite(long value) {
41+
writeLastRetry.setRelease(value);
42+
writeLastRequest.setRelease(System.currentTimeMillis());
43+
}
44+
45+
public void resetRead() {
46+
readLastRetry.setRelease(0);
47+
readLastRequest.setRelease(System.currentTimeMillis());
48+
}
49+
50+
public void resetWrite() {
51+
writeLastRetry.setRelease(0);
52+
writeLastRequest.setRelease(System.currentTimeMillis());
53+
}
54+
}

0 commit comments

Comments
 (0)