Skip to content

Commit 69cf13c

Browse files
authored
Perform port checks in parallel (#4463)
The checks will now also incorporate Awaitility.
1 parent b29716d commit 69cf13c

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

core/src/main/java/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,26 @@
66

77
import java.time.Duration;
88
import java.util.Set;
9+
import java.util.concurrent.ExecutorService;
10+
import java.util.concurrent.Executors;
11+
import java.util.concurrent.ThreadFactory;
912
import java.util.concurrent.TimeUnit;
13+
import java.util.concurrent.atomic.AtomicLong;
1014

1115
public abstract class AbstractWaitStrategy implements WaitStrategy {
1216

17+
static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(new ThreadFactory() {
18+
19+
private final AtomicLong COUNTER = new AtomicLong(0);
20+
21+
@Override
22+
public Thread newThread(Runnable r) {
23+
Thread thread = new Thread(r, "testcontainers-wait-" + COUNTER.getAndIncrement());
24+
thread.setDaemon(true);
25+
return thread;
26+
}
27+
});
28+
1329
private static final RateLimiter DOCKER_CLIENT_RATE_LIMITER = RateLimiterBuilder
1430
.newBuilder()
1531
.withRate(1, TimeUnit.SECONDS)

core/src/main/java/org/testcontainers/containers/wait/strategy/HostPortWaitStrategy.java

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
package org.testcontainers.containers.wait.strategy;
22

3+
import lombok.SneakyThrows;
34
import lombok.extern.slf4j.Slf4j;
4-
import org.rnorth.ducttape.TimeoutException;
5-
import org.rnorth.ducttape.unreliables.Unreliables;
5+
import org.awaitility.Awaitility;
66
import org.testcontainers.containers.ContainerLaunchException;
77
import org.testcontainers.containers.wait.internal.ExternalPortListeningCheck;
88
import org.testcontainers.containers.wait.internal.InternalCommandPortListeningCheck;
99

10+
import java.time.Duration;
11+
import java.time.Instant;
12+
import java.util.Arrays;
1013
import java.util.List;
1114
import java.util.Set;
1215
import java.util.concurrent.Callable;
16+
import java.util.concurrent.CancellationException;
17+
import java.util.concurrent.ExecutionException;
18+
import java.util.concurrent.Future;
1319
import java.util.concurrent.TimeUnit;
20+
import java.util.concurrent.TimeoutException;
1421
import java.util.stream.Collectors;
1522

1623
/**
@@ -22,6 +29,7 @@
2229
public class HostPortWaitStrategy extends AbstractWaitStrategy {
2330

2431
@Override
32+
@SneakyThrows(InterruptedException.class)
2533
protected void waitUntilReady() {
2634
final Set<Integer> externalLivenessCheckPorts = getLivenessCheckPorts();
2735
if (externalLivenessCheckPorts.isEmpty()) {
@@ -31,7 +39,6 @@ protected void waitUntilReady() {
3139
return;
3240
}
3341

34-
@SuppressWarnings("unchecked")
3542
List<Integer> exposedPorts = waitStrategyTarget.getExposedPorts();
3643

3744
final Set<Integer> internalPorts = getInternalPorts(externalLivenessCheckPorts, exposedPorts);
@@ -41,10 +48,43 @@ protected void waitUntilReady() {
4148
Callable<Boolean> externalCheck = new ExternalPortListeningCheck(waitStrategyTarget, externalLivenessCheckPorts);
4249

4350
try {
44-
Unreliables.retryUntilTrue((int) startupTimeout.getSeconds(), TimeUnit.SECONDS,
45-
() -> getRateLimiter().getWhenReady(() -> internalCheck.call() && externalCheck.call()));
51+
List<Future<Boolean>> futures = EXECUTOR.invokeAll(Arrays.asList(
52+
// Blocking
53+
() -> {
54+
Instant now = Instant.now();
55+
Boolean result = internalCheck.call();
56+
log.debug(
57+
"Internal port check {} for {} in {}",
58+
Boolean.TRUE.equals(result) ? "passed" : "failed",
59+
internalPorts,
60+
Duration.between(now, Instant.now())
61+
);
62+
return result;
63+
},
64+
// Polling
65+
() -> {
66+
Instant now = Instant.now();
67+
Awaitility.await()
68+
.pollInSameThread()
69+
.pollInterval(Duration.ofMillis(100))
70+
.pollDelay(Duration.ZERO)
71+
.forever()
72+
.until(externalCheck);
4673

47-
} catch (TimeoutException e) {
74+
log.debug(
75+
"External port check passed for {} mapped as {} in {}",
76+
internalPorts,
77+
externalLivenessCheckPorts,
78+
Duration.between(now, Instant.now())
79+
);
80+
return true;
81+
}
82+
), startupTimeout.getSeconds(), TimeUnit.SECONDS);
83+
84+
for (Future<Boolean> future : futures) {
85+
future.get(0, TimeUnit.SECONDS);
86+
}
87+
} catch (CancellationException | ExecutionException | TimeoutException e) {
4888
throw new ContainerLaunchException("Timed out waiting for container port to open (" +
4989
waitStrategyTarget.getHost() +
5090
" ports: " +

0 commit comments

Comments
 (0)