Skip to content

Commit 9daa0a7

Browse files
authored
Merge pull request #5 from sornerol/1-429_retry
Retry when receiving a 429 response code
2 parents 049bcb5 + 92a8c6d commit 9daa0a7

File tree

2 files changed

+100
-9
lines changed

2 files changed

+100
-9
lines changed

src/main/java/io/github/sornerol/chess/pubapi/client/PubApiClientBase.java

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import io.github.sornerol.chess.pubapi.client.enums.ResponseCode;
5+
import io.github.sornerol.chess.pubapi.client.enums.RetryStrategy;
56
import io.github.sornerol.chess.pubapi.exception.ChessComPubApiException;
67
import lombok.Getter;
78
import lombok.Setter;
89
import org.apache.commons.io.IOUtils;
9-
import org.apache.http.Header;
1010
import org.apache.http.client.methods.CloseableHttpResponse;
1111
import org.apache.http.client.methods.HttpGet;
1212
import org.apache.http.impl.client.CloseableHttpClient;
@@ -22,20 +22,68 @@
2222
*/
2323
abstract class PubApiClientBase {
2424

25+
/**
26+
* Default value for retryInterval.
27+
*/
28+
public static final Integer DEFAULT_RETRY_INTERVAL = 3;
29+
30+
/**
31+
* Default value for maxRetries.
32+
*/
33+
public static final Integer DEFAULT_MAX_RETRIES = 2;
34+
35+
/**
36+
* Default value for retryStrategy.
37+
*/
38+
public static final RetryStrategy DEFAULT_RETRY_STRATEGY = RetryStrategy.RETRY_N_TIMES;
39+
2540
/**
2641
* If supplied, the userAgent value will be passed in the User-Agent header when making requests to Chess.com.
2742
* By specifying this value with your contact information, Chess.com can contact you in the event they need to
2843
* block access for your application.
44+
*
2945
* @see <a href="https://www.chess.com/news/view/published-data-api#pubapi-general-rate-limits">Rate Limiting</a>
3046
*/
3147
@Getter
3248
@Setter
3349
private String userAgent;
3450

51+
/**
52+
* Number of seconds to wait before retrying if a request fails due to a 429 response code. If retryStrategy is set
53+
* to NEVER, this field does nothing.
54+
*
55+
* @see <a href="https://www.chess.com/news/view/published-data-api#pubapi-general-rate-limits">Rate Limiting</a>
56+
*/
57+
@Getter
58+
@Setter
59+
private Integer retryInterval;
60+
61+
/**
62+
* Maximum number of retries when retryStrategy is RETRY_N_TIMES. This does not include the initial attempt
63+
* (e.g. if maxRetries is 2, the client will make three total attempts before giving up).
64+
*
65+
* @see <a href="https://www.chess.com/news/view/published-data-api#pubapi-general-rate-limits">Rate Limiting</a>
66+
*/
67+
@Getter
68+
@Setter
69+
private Integer maxRetries;
70+
71+
/**
72+
* Defines the behavior to use when receiving a 429 response from the Chess.com API.
73+
*
74+
* @see <a href="https://www.chess.com/news/view/published-data-api#pubapi-general-rate-limits">Rate Limiting</a>
75+
*/
76+
@Getter
77+
@Setter
78+
private RetryStrategy retryStrategy;
79+
3580
private final CloseableHttpClient httpClient;
3681

3782
protected PubApiClientBase() {
3883
httpClient = HttpClients.createDefault();
84+
retryInterval = DEFAULT_RETRY_INTERVAL;
85+
maxRetries = DEFAULT_MAX_RETRIES;
86+
retryStrategy = DEFAULT_RETRY_STRATEGY;
3987
}
4088

4189
/**
@@ -70,15 +118,34 @@ protected String getRequest(String endpoint) throws IOException, ChessComPubApiE
70118
}
71119

72120
String responseBody;
73-
try (CloseableHttpResponse response = httpClient.execute(request)) {
74-
int statusCode = response.getStatusLine().getStatusCode();
75-
//TODO: Handle rate throttling more gracefully
76-
if (statusCode != ResponseCode.OK.getValue()) {
77-
throw new ChessComPubApiException("Error executing GET request: API returned status code " + statusCode);
121+
Integer attempts = 1;
122+
123+
boolean keepTrying = false;
124+
do {
125+
try (CloseableHttpResponse response = httpClient.execute(request)) {
126+
int statusCode = response.getStatusLine().getStatusCode();
127+
if (statusCode == ResponseCode.RATE_LIMIT_EXCEEDED.getValue() && shouldTryRequestAgain(attempts)) {
128+
keepTrying = true;
129+
attempts++;
130+
Thread.sleep(retryInterval * 1000);
131+
} else if (statusCode != ResponseCode.OK.getValue()) {
132+
throw new ChessComPubApiException("Error executing GET request: API returned status code " + statusCode);
133+
}
134+
InputStream inputStream = response.getEntity().getContent();
135+
responseBody = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
136+
} catch (InterruptedException e) {
137+
throw new ChessComPubApiException("Error while executing request: " + e.getMessage());
78138
}
79-
InputStream inputStream = response.getEntity().getContent();
80-
responseBody = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
81-
}
139+
} while (keepTrying);
140+
82141
return responseBody;
83142
}
143+
144+
private boolean shouldTryRequestAgain(Integer attemptsSoFar) {
145+
if (retryStrategy == RetryStrategy.RETRY_FOREVER) {
146+
return true;
147+
}
148+
149+
return retryStrategy == RetryStrategy.RETRY_N_TIMES && attemptsSoFar <= maxRetries;
150+
}
84151
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.github.sornerol.chess.pubapi.client.enums;
2+
3+
/**
4+
* Specifies a retry strategy for the base client. When the Chess.com API returns a 429 response code, the base client
5+
* will attempt to retry the request according to the specified retry strategy.
6+
*/
7+
public enum RetryStrategy {
8+
9+
/**
10+
* Don't retry the request. In this case, the client will throw an exception if the request fails due to a 429 response.
11+
*/
12+
NEVER,
13+
14+
/**
15+
* Retry a specified number of times. The number of times to retry is specified in the client's maxRetries property. This is the default behavior
16+
*/
17+
RETRY_N_TIMES,
18+
19+
/**
20+
* If a request fails due to a 429 response, keep retrying indefinitely. This behavior is not recommended, since it could theoretically
21+
* lead to an infinite loop.
22+
*/
23+
RETRY_FOREVER
24+
}

0 commit comments

Comments
 (0)