huaxingao commented on code in PR #18070:
URL: https://github.com/apache/iceberg/pull/18070#discussion_r4042731661
##########
core/src/main/java/org/apache/iceberg/rest/ExponentialHttpRequestRetryStrategy.java:
##########
@@ -183,22 +197,54 @@ public TimeValue getRetryInterval(HttpResponse response,
int execCount, HttpCont
}
}
- int delayMillis = 1000 * (int) Math.min(Math.pow(2.0, (long) execCount -
1.0), 64.0);
+ long delayMillis = nextBackoffMillis(execCount);
int jitter = ThreadLocalRandom.current().nextInt(Math.max(1, (int)
(delayMillis * 0.1)));
return TimeValue.ofMilliseconds(delayMillis + jitter);
}
+ private static boolean isRetrySafe(HttpRequest request) {
+ return request != null
+ && (Method.isIdempotent(request.getMethod())
+ || request.containsHeader(RESTUtil.IDEMPOTENCY_KEY_HEADER));
+ }
+
private boolean shouldRetryIdempotent(HttpRequest request, int responseCode)
{
if (request == null) {
return false;
}
- // A request is retry-safe if its HTTP method is idempotent or it carries
an Idempotency-Key
- // header (which lets the server replay a finalized result on retry).
- boolean retrySafe =
- Method.isIdempotent(request.getMethod())
- || request.containsHeader(RESTUtil.IDEMPOTENCY_KEY_HEADER);
- return retrySafe && idempotentRetriableCodes.contains(responseCode);
+ return isRetrySafe(request) &&
idempotentRetriableCodes.contains(responseCode);
+ }
+
+ private static long nextBackoffMillis(int execCount) {
+ return 1000L * (long) Math.min(Math.pow(2.0, (long) execCount - 1.0),
64.0);
+ }
+
+ private boolean wouldExceedKeyLifetime(
+ HttpRequest request, HttpResponse response, int execCount, HttpContext
context) {
+ if (keyLifetime == null
+ || context == null
+ || request == null
+ || !request.containsHeader(RESTUtil.IDEMPOTENCY_KEY_HEADER)) {
+ return false;
+ }
+
+ long now = System.currentTimeMillis();
+ Object attr = context.getAttribute(FIRST_ATTEMPT_EPOCH);
+ long firstAttemptMillis;
+ if (attr instanceof Long) {
+ firstAttemptMillis = (Long) attr;
+ } else {
+ firstAttemptMillis = now;
+ context.setAttribute(FIRST_ATTEMPT_EPOCH, firstAttemptMillis);
Review Comment:
The start time is saved only after the first attempt fails, so a slow first
attempt isn't counted and we may retry after the key has expired. Should this
be recorded when the request is first sent?
##########
core/src/test/java/org/apache/iceberg/rest/TestExponentialHttpRequestRetryStrategy.java:
##########
@@ -249,4 +250,64 @@ public void
testRetryDoesNotHappenForNonIdempotentMethodWithoutIdempotencyKey(in
context.setRequest(new BasicHttpRequest("POST", "/"));
assertThat(retryStrategy.retryRequest(response, 3, context)).isFalse();
}
+
+ @Test
+ public void testKeyedRetryAbandonedWhenLifetimeExceeded() {
+ HttpRequestRetryStrategy strategy =
+ new ExponentialHttpRequestRetryStrategy(5, Duration.ofSeconds(1));
+ BasicHttpResponse response = new
BasicHttpResponse(HttpStatus.SC_SERVICE_UNAVAILABLE, "busy");
+ response.addHeader(new BasicHeader(HttpHeaders.RETRY_AFTER, "3600"));
+ HttpClientContext context = HttpClientContext.create();
+ BasicHttpRequest request = new BasicHttpRequest("POST", "/");
+ request.addHeader(RESTUtil.IDEMPOTENCY_KEY_HEADER, "key-abandon");
+ context.setRequest(request);
+ assertThat(strategy.retryRequest(response, 1, context)).isFalse();
Review Comment:
I think this only tests that one `Retry-After` is longer than `keyLifetime`.
`context` is new and `retryRequest` is called once, so `wouldExceedKeyLifetime`
always takes the `else` branch: `FIRST_ATTEMPT_EPOCH` is never read back,
`firstAttemptMillis` is set to `now`, and `now - firstAttemptMillis` is always
0. I deleted the `context.setAttribute(FIRST_ATTEMPT_EPOCH,
firstAttemptMillis)` line locally and all five new tests still passed.
Can you call `retryRequest` twice on the same `context` with time passing in
between? With a 2s `keyLifetime` and `Retry-After: 1`:
```
assertThat(strategy.retryRequest(response, 1, context)).isTrue();
Thread.sleep(1100);
assertThat(strategy.retryRequest(response, 2, context)).isFalse();
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]