Skip to content

Commit 3edbe3e

Browse files
committed
Fix waitForSidecar to respect timeout.
Signed-off-by: Artur Souza <[email protected]>
1 parent 7dcab0b commit 3edbe3e

File tree

3 files changed

+111
-49
lines changed

3 files changed

+111
-49
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2024 The Dapr Authors
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
14+
package io.dapr.it.resiliency;
15+
16+
import io.dapr.client.DaprClient;
17+
import io.dapr.client.DaprClientImpl;
18+
import io.dapr.client.resiliency.ResiliencyOptions;
19+
import io.dapr.it.BaseIT;
20+
import io.dapr.it.DaprRun;
21+
import io.dapr.it.ToxiProxyRun;
22+
import org.junit.jupiter.api.AfterAll;
23+
import org.junit.jupiter.api.BeforeAll;
24+
import org.junit.jupiter.api.Test;
25+
26+
import java.nio.charset.StandardCharsets;
27+
import java.time.Duration;
28+
import java.util.Base64;
29+
import java.util.UUID;
30+
import java.util.concurrent.atomic.AtomicInteger;
31+
32+
import static org.junit.jupiter.api.Assertions.assertEquals;
33+
import static org.junit.jupiter.api.Assertions.assertThrows;
34+
import static org.junit.jupiter.api.Assertions.assertTrue;
35+
36+
/**
37+
* Test SDK resiliency.
38+
*/
39+
public class WaitForSidecarIT extends BaseIT {
40+
41+
// Use a number large enough to make sure it will respect the entire timeout.
42+
private static final Duration LATENCY = Duration.ofSeconds(5);
43+
44+
private static final Duration JITTER = Duration.ofSeconds(0);
45+
46+
private static DaprRun daprRun;
47+
48+
private static ToxiProxyRun toxiProxyRun;
49+
50+
private static DaprRun daprNotRunning;
51+
52+
@BeforeAll
53+
public static void init() throws Exception {
54+
daprRun = startDaprApp(WaitForSidecarIT.class.getSimpleName(), 5000);
55+
daprNotRunning = startDaprApp(WaitForSidecarIT.class.getSimpleName()+"NotRunning", 5000);
56+
daprNotRunning.stop();
57+
58+
toxiProxyRun = new ToxiProxyRun(daprRun, LATENCY, JITTER);
59+
toxiProxyRun.start();
60+
}
61+
62+
@Test
63+
public void waitSucceeds() throws Exception {
64+
try(var client = daprRun.newDaprClient()) {
65+
client.waitForSidecar(5000).block();
66+
}
67+
}
68+
69+
@Test
70+
public void waitTimeout() {
71+
int timeoutInMillis = (int)LATENCY.minusMillis(100).toMillis();
72+
long started = System.currentTimeMillis();
73+
assertThrows(RuntimeException.class, () -> {
74+
try(var client = toxiProxyRun.newDaprClientBuilder().build()) {
75+
client.waitForSidecar(timeoutInMillis).block();
76+
}
77+
});
78+
long duration = System.currentTimeMillis() - started;
79+
assertTrue(duration >= timeoutInMillis);
80+
}
81+
82+
@Test
83+
public void waitSlow() throws Exception {
84+
int timeoutInMillis = (int)LATENCY.plusMillis(100).toMillis();
85+
long started = System.currentTimeMillis();
86+
try(var client = toxiProxyRun.newDaprClientBuilder().build()) {
87+
client.waitForSidecar(timeoutInMillis).block();
88+
}
89+
long duration = System.currentTimeMillis() - started;
90+
assertTrue(duration >= LATENCY.toMillis());
91+
}
92+
93+
@Test
94+
public void waitNotRunningTimeout() {
95+
// Does not make this number too smaller since bug does not repro when <= 2.5s.
96+
// This has to do with a previous bug in the implementation.
97+
int timeoutMilliseconds = 5000;
98+
long started = System.currentTimeMillis();
99+
assertThrows(RuntimeException.class, () -> {
100+
try(var client = daprNotRunning.newDaprClientBuilder().build()) {
101+
client.waitForSidecar(timeoutMilliseconds).block();
102+
}
103+
});
104+
long duration = System.currentTimeMillis() - started;
105+
assertTrue(duration >= timeoutMilliseconds);
106+
}
107+
}

sdk/src/main/java/io/dapr/client/DaprClientImpl.java

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -273,53 +273,13 @@ public <T extends AbstractStub<T>> T newGrpcStub(String appId, Function<Channel,
273273
@Override
274274
public Mono<Void> waitForSidecar(int timeoutInMilliseconds) {
275275
String[] pathSegments = new String[] { DaprHttp.API_VERSION, "healthz", "outbound"};
276-
int maxRetries = 5;
277-
278-
Retry retrySpec = Retry
279-
.fixedDelay(maxRetries, Duration.ofMillis(500))
280-
.doBeforeRetry(retrySignal -> {
281-
System.out.println("Retrying component health check...");
282-
});
283-
284-
/*
285-
NOTE: (Cassie) Uncomment this once it actually gets implemented:
286-
https://github.com/grpc/grpc-java/issues/4359
287-
288-
int maxChannelStateRetries = 5;
289-
290-
// Retry logic for checking the channel state
291-
Retry channelStateRetrySpec = Retry
292-
.fixedDelay(maxChannelStateRetries, Duration.ofMillis(500))
293-
.doBeforeRetry(retrySignal -> {
294-
System.out.println("Retrying channel state check...");
295-
});
296-
*/
297276

298277
// Do the Dapr Http endpoint check to have parity with Dotnet
299278
Mono<DaprHttp.Response> responseMono = this.httpClient.invokeApi(DaprHttp.HttpMethods.GET.name(), pathSegments,
300279
null, "", null, null);
301280

302281
return responseMono
303-
.retryWhen(retrySpec)
304-
/*
305-
NOTE: (Cassie) Uncomment this once it actually gets implemented:
306-
https://github.com/grpc/grpc-java/issues/4359
307-
.flatMap(response -> {
308-
// Check the status code
309-
int statusCode = response.getStatusCode();
310-
311-
// Check if the channel's state is READY
312-
return Mono.defer(() -> {
313-
if (this.channel.getState(true) == ConnectivityState.READY) {
314-
// Return true if the status code is in the 2xx range
315-
if (statusCode >= 200 && statusCode < 300) {
316-
return Mono.empty(); // Continue with the flow
317-
}
318-
}
319-
return Mono.error(new RuntimeException("Health check failed"));
320-
}).retryWhen(channelStateRetrySpec);
321-
})
322-
*/
282+
.retryWhen(Retry.indefinitely())
323283
.timeout(Duration.ofMillis(timeoutInMilliseconds))
324284
.onErrorResume(DaprException.class, e ->
325285
Mono.error(new RuntimeException(e)))

sdk/src/test/java/io/dapr/client/DaprClientHttpTest.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,16 +133,11 @@ public void waitForSidecarBadHealthCheck() throws Exception {
133133
.times(6)
134134
.respond(404, ResponseBody.create("Not Found", MediaType.get("application/json")));
135135

136-
// retry the max allowed retries (5 times)
136+
// it will timeout.
137137
StepVerifier.create(daprClientHttp.waitForSidecar(5000))
138138
.expectSubscription()
139-
.expectErrorMatches(throwable -> {
140-
if (throwable instanceof RuntimeException) {
141-
return "Retries exhausted: 5/5".equals(throwable.getMessage());
142-
}
143-
return false;
144-
})
145-
.verify(Duration.ofSeconds(20));
139+
.expectError()
140+
.verify(Duration.ofMillis(6000));
146141
}
147142

148143
@Test

0 commit comments

Comments
 (0)