Skip to content

Commit 071d560

Browse files
singhpk234RussellSpitzer
authored andcommitted
REST: Revert apache#12818 and additionally stop retrying on 502/504 (apache#13352)
1 parent 35912d2 commit 071d560

File tree

6 files changed

+25
-108
lines changed

6 files changed

+25
-108
lines changed

core/src/main/java/org/apache/iceberg/rest/ErrorHandlers.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -91,19 +91,7 @@ public void accept(ErrorResponse error) {
9191
case 404:
9292
throw new NoSuchTableException("%s", error.message());
9393
case 409:
94-
if (error.wasRetried()) {
95-
// If a retried request finally fails with 409,
96-
// the IRC service may have persisted the commit
97-
// despite initial 5xx errors, resulting in a self-conflict on retry
98-
// due to the base changing.
99-
// Mark this failure as commit state unknown rather than failed to prevent file cleanup.
100-
throw new CommitStateUnknownException(
101-
new RESTException(
102-
"Commit status unknown, due to retries: %s: %s",
103-
error.code(), error.message()));
104-
} else {
105-
throw new CommitFailedException("Commit failed: %s", error.message());
106-
}
94+
throw new CommitFailedException("Commit failed: %s", error.message());
10795
case 500:
10896
case 502:
10997
case 504:

core/src/main/java/org/apache/iceberg/rest/ExponentialHttpRequestRetryStrategy.java

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@
6060
*
6161
* <ul>
6262
* <li>SC_TOO_MANY_REQUESTS (429)
63-
* <li>SC_BAD_GATEWAY (502)
6463
* <li>SC_SERVICE_UNAVAILABLE (503)
65-
* <li>SC_GATEWAY_TIMEOUT (504)
6664
* </ul>
6765
*
6866
* Most code and behavior is taken from {@link
@@ -79,11 +77,7 @@ class ExponentialHttpRequestRetryStrategy implements HttpRequestRetryStrategy {
7977
maximumRetries > 0, "Cannot set retries to %s, the value must be positive", maximumRetries);
8078
this.maxRetries = maximumRetries;
8179
this.retriableCodes =
82-
ImmutableSet.of(
83-
HttpStatus.SC_TOO_MANY_REQUESTS,
84-
HttpStatus.SC_SERVICE_UNAVAILABLE,
85-
HttpStatus.SC_BAD_GATEWAY,
86-
HttpStatus.SC_GATEWAY_TIMEOUT);
80+
ImmutableSet.of(HttpStatus.SC_TOO_MANY_REQUESTS, HttpStatus.SC_SERVICE_UNAVAILABLE);
8781
this.nonRetriableExceptions =
8882
ImmutableSet.of(
8983
InterruptedIOException.class,
@@ -118,20 +112,12 @@ public boolean retryRequest(
118112
}
119113

120114
// Retry if the request is considered idempotent
121-
boolean shouldRetry = Method.isIdempotent(request.getMethod());
122-
if (shouldRetry && context != null) {
123-
context.setAttribute("was-retried", Boolean.TRUE);
124-
}
125-
return shouldRetry;
115+
return Method.isIdempotent(request.getMethod());
126116
}
127117

128118
@Override
129119
public boolean retryRequest(HttpResponse response, int execCount, HttpContext context) {
130-
boolean shouldRetry = execCount <= maxRetries && retriableCodes.contains(response.getCode());
131-
if (shouldRetry && context != null) {
132-
context.setAttribute("was-retried", Boolean.TRUE);
133-
}
134-
return shouldRetry;
120+
return execCount <= maxRetries && retriableCodes.contains(response.getCode());
135121
}
136122

137123
@Override

core/src/main/java/org/apache/iceberg/rest/HTTPClient.java

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@
4545
import org.apache.hc.core5.http.impl.EnglishReasonPhraseCatalog;
4646
import org.apache.hc.core5.http.io.entity.EntityUtils;
4747
import org.apache.hc.core5.http.io.entity.StringEntity;
48-
import org.apache.hc.core5.http.protocol.BasicHttpContext;
49-
import org.apache.hc.core5.http.protocol.HttpContext;
5048
import org.apache.hc.core5.io.CloseMode;
5149
import org.apache.iceberg.IcebergBuild;
5250
import org.apache.iceberg.exceptions.RESTException;
@@ -177,10 +175,7 @@ private static ErrorResponse buildDefaultErrorResponse(CloseableHttpResponse res
177175
// Process a failed response through the provided errorHandler, and throw a RESTException if the
178176
// provided error handler doesn't already throw.
179177
private static void throwFailure(
180-
CloseableHttpResponse response,
181-
String responseBody,
182-
Consumer<ErrorResponse> errorHandler,
183-
Object wasRetried) {
178+
CloseableHttpResponse response, String responseBody, Consumer<ErrorResponse> errorHandler) {
184179
ErrorResponse errorResponse = null;
185180

186181
if (responseBody != null) {
@@ -217,19 +212,10 @@ private static void throwFailure(
217212
errorResponse = buildDefaultErrorResponse(response);
218213
}
219214

220-
ErrorResponse enrichedErrorResponse =
221-
ErrorResponse.builder()
222-
.wasRetried(wasRetried == Boolean.TRUE)
223-
.responseCode(errorResponse.code())
224-
.withMessage(errorResponse.message())
225-
.withType(errorResponse.type())
226-
.withStackTrace(errorResponse.stack())
227-
.build();
228-
229-
errorHandler.accept(enrichedErrorResponse);
215+
errorHandler.accept(errorResponse);
230216

231217
// Throw an exception in case the provided error handler does not throw.
232-
throw new RESTException("Unhandled error: %s", enrichedErrorResponse);
218+
throw new RESTException("Unhandled error: %s", errorResponse);
233219
}
234220

235221
@Override
@@ -292,8 +278,7 @@ protected <T extends RESTResponse> T execute(
292278
request.setEntity(new StringEntity(encodedBody));
293279
}
294280

295-
HttpContext context = new BasicHttpContext();
296-
try (CloseableHttpResponse response = httpClient.execute(request, context)) {
281+
try (CloseableHttpResponse response = httpClient.execute(request)) {
297282
Map<String, String> respHeaders = Maps.newHashMap();
298283
for (Header header : response.getHeaders()) {
299284
respHeaders.put(header.getName(), header.getValue());
@@ -311,7 +296,7 @@ protected <T extends RESTResponse> T execute(
311296

312297
if (!isSuccessful(response)) {
313298
// The provided error handler is expected to throw, but a RESTException is thrown if not.
314-
throwFailure(response, responseBody, errorHandler, context.getAttribute("was-retried"));
299+
throwFailure(response, responseBody, errorHandler);
315300
}
316301

317302
if (responseBody == null) {

core/src/main/java/org/apache/iceberg/rest/responses/ErrorResponse.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,12 @@ public class ErrorResponse implements RESTResponse {
3232
private final String type;
3333
private final int code;
3434
private final List<String> stack;
35-
private final boolean wasRetried;
3635

37-
private ErrorResponse(
38-
String message, String type, int code, List<String> stack, boolean wasRetried) {
36+
private ErrorResponse(String message, String type, int code, List<String> stack) {
3937
this.message = message;
4038
this.type = type;
4139
this.code = code;
4240
this.stack = stack;
43-
this.wasRetried = wasRetried;
4441
validate();
4542
}
4643

@@ -65,10 +62,6 @@ public List<String> stack() {
6562
return stack;
6663
}
6764

68-
public boolean wasRetried() {
69-
return wasRetried;
70-
}
71-
7265
@Override
7366
public String toString() {
7467
StringBuilder sb = new StringBuilder();
@@ -99,7 +92,6 @@ public static class Builder {
9992
private String type;
10093
private Integer code;
10194
private List<String> stack;
102-
private boolean wasRetried;
10395

10496
private Builder() {}
10597

@@ -134,14 +126,9 @@ public Builder responseCode(Integer responseCode) {
134126
return this;
135127
}
136128

137-
public Builder wasRetried(boolean hasBeenRetried) {
138-
this.wasRetried = hasBeenRetried;
139-
return this;
140-
}
141-
142129
public ErrorResponse build() {
143130
Preconditions.checkArgument(code != null, "Invalid response, missing field: code");
144-
return new ErrorResponse(message, type, code, stack, wasRetried);
131+
return new ErrorResponse(message, type, code, stack);
145132
}
146133
}
147134
}

core/src/test/java/org/apache/iceberg/rest/TestExponentialHttpRequestRetryStrategy.java

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@
3737
import org.apache.hc.core5.http.HttpHeaders;
3838
import org.apache.hc.core5.http.HttpResponse;
3939
import org.apache.hc.core5.http.message.BasicHttpResponse;
40-
import org.apache.hc.core5.http.protocol.BasicHttpContext;
41-
import org.apache.hc.core5.http.protocol.HttpContext;
4240
import org.junit.jupiter.api.Test;
4341
import org.junit.jupiter.params.ParameterizedTest;
4442
import org.junit.jupiter.params.provider.ValueSource;
@@ -82,20 +80,14 @@ public void exponentialRetry() {
8280

8381
@Test
8482
public void basicRetry() {
85-
HttpContext context503 = new BasicHttpContext();
8683
BasicHttpResponse response503 = new BasicHttpResponse(503, "Oopsie");
87-
assertThat(retryStrategy.retryRequest(response503, 3, context503)).isTrue();
88-
assertThat(context503.getAttribute("was-retried") == Boolean.TRUE).isTrue();
84+
assertThat(retryStrategy.retryRequest(response503, 3, null)).isTrue();
8985

90-
HttpContext context429 = new BasicHttpContext();
9186
BasicHttpResponse response429 = new BasicHttpResponse(429, "Oopsie");
92-
assertThat(retryStrategy.retryRequest(response429, 3, context429)).isTrue();
93-
assertThat(context429.getAttribute("was-retried") == Boolean.TRUE).isTrue();
87+
assertThat(retryStrategy.retryRequest(response429, 3, null)).isTrue();
9488

95-
HttpContext context404 = new BasicHttpContext();
9689
BasicHttpResponse response404 = new BasicHttpResponse(404, "Oopsie");
97-
assertThat(retryStrategy.retryRequest(response404, 3, context404)).isFalse();
98-
assertThat(context429.getAttribute("was-retried") == Boolean.TRUE).isTrue();
90+
assertThat(retryStrategy.retryRequest(response404, 3, null)).isFalse();
9991
}
10092

10193
@Test
@@ -163,9 +155,8 @@ public void noRetryOnAbortedRequests() {
163155
@Test
164156
public void retryOnNonAbortedRequests() {
165157
HttpGet request = new HttpGet("/");
166-
HttpContext context = new BasicHttpContext();
167158

168-
assertThat(retryStrategy.retryRequest(request, new IOException(), 1, context)).isTrue();
159+
assertThat(retryStrategy.retryRequest(request, new IOException(), 1, null)).isTrue();
169160
}
170161

171162
@Test
@@ -206,17 +197,17 @@ public void invalidRetryAfterHeader() {
206197
.isBetween(4000L, 5000L);
207198
}
208199

209-
@Test
210-
public void testRetryBadGateway() {
211-
HttpContext context = new BasicHttpContext();
212-
BasicHttpResponse response502 = new BasicHttpResponse(502, "Bad gateway failure");
213-
assertThat(retryStrategy.retryRequest(response502, 3, context)).isTrue();
200+
@ParameterizedTest
201+
@ValueSource(ints = {429, 503})
202+
public void testRetryHappensOnAcceptableStatusCodes(int statusCode) {
203+
BasicHttpResponse response = new BasicHttpResponse(statusCode, String.valueOf(statusCode));
204+
assertThat(retryStrategy.retryRequest(response, 3, null)).isTrue();
214205
}
215206

216-
@Test
217-
public void testRetryGatewayTimeout() {
218-
HttpContext context = new BasicHttpContext();
219-
BasicHttpResponse response504 = new BasicHttpResponse(504, "Gateway timeout");
220-
assertThat(retryStrategy.retryRequest(response504, 3, context)).isTrue();
207+
@ParameterizedTest
208+
@ValueSource(ints = {500, 502, 504})
209+
public void testRetryDoesNotHappenOnUnacceptableStatusCodes(int statusCode) {
210+
BasicHttpResponse response = new BasicHttpResponse(statusCode, String.valueOf(statusCode));
211+
assertThat(retryStrategy.retryRequest(response, 3, null)).isFalse();
221212
}
222213
}

core/src/test/java/org/apache/iceberg/rest/TestRESTCatalog.java

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
import org.apache.iceberg.catalog.TableCommit;
6363
import org.apache.iceberg.catalog.TableIdentifier;
6464
import org.apache.iceberg.exceptions.CommitFailedException;
65-
import org.apache.iceberg.exceptions.CommitStateUnknownException;
6665
import org.apache.iceberg.exceptions.NotAuthorizedException;
6766
import org.apache.iceberg.exceptions.NotFoundException;
6867
import org.apache.iceberg.exceptions.ServiceFailureException;
@@ -93,7 +92,6 @@
9392
import org.eclipse.jetty.servlet.ServletContextHandler;
9493
import org.eclipse.jetty.servlet.ServletHolder;
9594
import org.junit.jupiter.api.AfterEach;
96-
import org.junit.jupiter.api.Assertions;
9795
import org.junit.jupiter.api.BeforeEach;
9896
import org.junit.jupiter.api.Test;
9997
import org.junit.jupiter.api.io.TempDir;
@@ -2650,24 +2648,6 @@ public void testTableExistsFallbackToGETRequest() {
26502648
ConfigResponse.builder().withEndpoints(ImmutableList.of(Endpoint.V1_LOAD_TABLE)).build());
26512649
}
26522650

2653-
@Test
2654-
public void testErrorHandlingForConflicts() {
2655-
Consumer<ErrorResponse> errorResponseConsumer = ErrorHandlers.tableCommitHandler();
2656-
// server returning 409 with client without retrying
2657-
ErrorResponse errorResponse409WithoutRetries =
2658-
ErrorResponse.builder().responseCode(409).wasRetried(false).build();
2659-
Assertions.assertThrows(
2660-
CommitFailedException.class,
2661-
() -> errorResponseConsumer.accept(errorResponse409WithoutRetries));
2662-
2663-
// server returning 409, with retries.
2664-
ErrorResponse errorResponse409WithRetries =
2665-
ErrorResponse.builder().responseCode(409).wasRetried(true).build();
2666-
Assertions.assertThrows(
2667-
CommitStateUnknownException.class,
2668-
() -> errorResponseConsumer.accept(errorResponse409WithRetries));
2669-
}
2670-
26712651
private void verifyTableExistsFallbackToGETRequest(ConfigResponse configResponse) {
26722652
RESTCatalogAdapter adapter =
26732653
Mockito.spy(

0 commit comments

Comments
 (0)