Skip to content

Commit f1185a0

Browse files
shawkinsmanusa
authored andcommitted
fix #5095: moving the enforcement to requestTimeout
1 parent b185e93 commit f1185a0

File tree

4 files changed

+37
-15
lines changed

4 files changed

+37
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### 6.7-SNAPSHOT
44

55
#### Bugs
6+
* Fix #5095: moving the enforcement to requestTimeout
67
* Fix #5102: wait on scale to 0 was not completing
78

89
#### Improvements

kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpClient.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,18 @@
3333
import java.util.Optional;
3434
import java.util.concurrent.CompletableFuture;
3535
import java.util.concurrent.CompletionException;
36+
import java.util.concurrent.Future;
3637
import java.util.concurrent.TimeUnit;
38+
import java.util.concurrent.TimeoutException;
3739
import java.util.function.BiConsumer;
3840
import java.util.function.Function;
3941
import java.util.function.Supplier;
4042

4143
public abstract class StandardHttpClient<C extends HttpClient, F extends HttpClient.Factory, T extends StandardHttpClientBuilder<C, F, ?>>
4244
implements HttpClient, RequestTags {
4345

46+
private static final long ADDITIONAL_REQEUST_TIMEOUT = TimeUnit.SECONDS.toMillis(5);
47+
4448
private static final Logger LOG = LoggerFactory.getLogger(StandardHttpClient.class);
4549

4650
protected StandardHttpClientBuilder<C, F, T> builder;
@@ -136,14 +140,25 @@ private CompletableFuture<HttpResponse<AsyncBody>> consumeBytesOnce(HttpRequest
136140
};
137141
}
138142

143+
public <V> CompletableFuture<V> orTimeout(CompletableFuture<V> future, RequestConfig requestConfig) {
144+
int timeout = Optional.ofNullable(requestConfig).map(RequestConfig::getRequestTimeout).orElse(0);
145+
if (timeout > 0) {
146+
Future<?> scheduled = Utils.schedule(Runnable::run, () -> future.completeExceptionally(new TimeoutException()),
147+
timeout + ADDITIONAL_REQEUST_TIMEOUT, TimeUnit.MILLISECONDS);
148+
future.whenComplete((v, t) -> scheduled.cancel(true));
149+
}
150+
return future;
151+
}
152+
139153
/**
140154
* Will retry the action if needed based upon the retry settings provided by the ExponentialBackoffIntervalCalculator.
141155
*/
142156
protected <V> void retryWithExponentialBackoff(CompletableFuture<V> result,
143157
Supplier<CompletableFuture<V>> action, URI uri, Function<V, Integer> codeExtractor,
144-
java.util.function.Consumer<V> cancel, ExponentialBackoffIntervalCalculator retryIntervalCalculator) {
158+
java.util.function.Consumer<V> cancel, ExponentialBackoffIntervalCalculator retryIntervalCalculator,
159+
RequestConfig requestConfig) {
145160

146-
action.get()
161+
orTimeout(action.get(), requestConfig)
147162
.whenComplete((response, throwable) -> {
148163
if (retryIntervalCalculator.shouldRetry() && !result.isDone()) {
149164
long retryInterval = retryIntervalCalculator.nextReconnectInterval();
@@ -168,7 +183,8 @@ protected <V> void retryWithExponentialBackoff(CompletableFuture<V> result,
168183
}
169184
if (retry) {
170185
Utils.schedule(Runnable::run,
171-
() -> retryWithExponentialBackoff(result, action, uri, codeExtractor, cancel, retryIntervalCalculator),
186+
() -> retryWithExponentialBackoff(result, action, uri, codeExtractor, cancel, retryIntervalCalculator,
187+
requestConfig),
172188
retryInterval,
173189
TimeUnit.MILLISECONDS);
174190
return;
@@ -181,8 +197,9 @@ protected <V> void retryWithExponentialBackoff(CompletableFuture<V> result,
181197
protected <V> void retryWithExponentialBackoff(CompletableFuture<V> result,
182198
Supplier<CompletableFuture<V>> action, URI uri, Function<V, Integer> codeExtractor,
183199
java.util.function.Consumer<V> cancel) {
200+
RequestConfig requestConfig = getTag(RequestConfig.class);
184201
retryWithExponentialBackoff(result, action, uri, codeExtractor, cancel,
185-
ExponentialBackoffIntervalCalculator.from(getTag(RequestConfig.class)));
202+
ExponentialBackoffIntervalCalculator.from(requestConfig), requestConfig);
186203
}
187204

188205
@Override

kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/StandardHttpClientTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import io.fabric8.kubernetes.client.RequestConfigBuilder;
1919
import io.fabric8.kubernetes.client.http.WebSocket.Listener;
20+
import org.awaitility.Awaitility;
2021
import org.junit.jupiter.api.BeforeEach;
2122
import org.junit.jupiter.api.Test;
2223
import org.mockito.Mockito;
@@ -212,4 +213,18 @@ void testClosePreviousBeforeRetry() throws Exception {
212213
assertEquals(2, client.getRespFutures().size());
213214
}
214215

216+
@Test
217+
void testRequestTimeout() throws Exception {
218+
client = client.newBuilder().tag(new RequestConfigBuilder()
219+
.withRequestTimeout(1).build())
220+
.build();
221+
222+
CompletableFuture<HttpResponse<AsyncBody>> consumeFuture = client.consumeBytes(
223+
client.newHttpRequestBuilder().uri("http://localhost").build(),
224+
(value, asyncBody) -> {
225+
});
226+
227+
Awaitility.await().atMost(10, TimeUnit.SECONDS).until(consumeFuture::isDone);
228+
}
229+
215230
}

kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,9 @@
5959
import java.util.Objects;
6060
import java.util.concurrent.CompletableFuture;
6161
import java.util.concurrent.ExecutionException;
62-
import java.util.concurrent.TimeUnit;
63-
import java.util.concurrent.TimeoutException;
6462

6563
public class OperationSupport {
6664

67-
private static final long ADDITIONAL_REQEUST_TIMEOUT = TimeUnit.SECONDS.toMillis(5);
6865
private static final String FIELD_MANAGER_PARAM = "?fieldManager=";
6966
public static final String JSON = "application/json";
7067
public static final String JSON_PATCH = "application/json-patch+json";
@@ -501,11 +498,6 @@ protected <T> T handleRawGet(URL resourceUrl, Class<T> type) throws IOException
501498
*/
502499
protected <T> T waitForResult(CompletableFuture<T> future) throws IOException {
503500
try {
504-
// since readTimeout may not be enforced in a timely manner at the httpclient, we'll
505-
// enforce a higher level timeout with a small amount of padding to account for possible queuing
506-
if (getRequestConfig().getRequestTimeout() > 0) {
507-
return future.get(getRequestConfig().getRequestTimeout() + ADDITIONAL_REQEUST_TIMEOUT, TimeUnit.MILLISECONDS);
508-
}
509501
return future.get();
510502
} catch (InterruptedException e) {
511503
Thread.currentThread().interrupt();
@@ -525,9 +517,6 @@ protected <T> T waitForResult(CompletableFuture<T> future) throws IOException {
525517
throw ((KubernetesClientException) t).copyAsCause();
526518
}
527519
throw new KubernetesClientException(t.getMessage(), t);
528-
} catch (TimeoutException e) {
529-
future.cancel(true);
530-
throw KubernetesClientException.launderThrowable(e);
531520
}
532521
}
533522

0 commit comments

Comments
 (0)