Skip to content

Commit 137c510

Browse files
authored
speed up port detection by running the checks as a single command (#1782)
1 parent 827c4c5 commit 137c510

File tree

4 files changed

+33
-30
lines changed

4 files changed

+33
-30
lines changed

core/src/main/java/org/testcontainers/containers/wait/internal/ExternalPortListeningCheck.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ public class ExternalPortListeningCheck implements Callable<Boolean> {
2020
public Boolean call() {
2121
String address = containerState.getContainerIpAddress();
2222

23-
for (Integer externalPort : externalLivenessCheckPorts) {
23+
externalLivenessCheckPorts.parallelStream().forEach(externalPort -> {
2424
try {
2525
new Socket(address, externalPort).close();
2626
} catch (IOException e) {
2727
throw new IllegalStateException("Socket not listening yet: " + externalPort);
2828
}
29-
}
29+
});
3030
return true;
3131
}
3232
}
Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package org.testcontainers.containers.wait.internal;
22

33
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.testcontainers.containers.Container.ExecResult;
46
import org.testcontainers.containers.ExecInContainerPattern;
57
import org.testcontainers.containers.wait.strategy.WaitStrategyTarget;
68

9+
import java.time.Duration;
10+
import java.time.Instant;
711
import java.util.Set;
812

913
import static java.lang.String.format;
@@ -12,37 +16,34 @@
1216
* Mechanism for testing that a socket is listening when run from the container being checked.
1317
*/
1418
@RequiredArgsConstructor
19+
@Slf4j
1520
public class InternalCommandPortListeningCheck implements java.util.concurrent.Callable<Boolean> {
1621

1722
private final WaitStrategyTarget waitStrategyTarget;
1823
private final Set<Integer> internalPorts;
1924

2025
@Override
2126
public Boolean call() {
22-
for (Integer internalPort : internalPorts) {
23-
tryPort(internalPort);
27+
String command = "true";
28+
29+
for (int internalPort : internalPorts) {
30+
command += " && ";
31+
command += " (";
32+
command += format("cat /proc/net/tcp{,6} | awk '{print $2}' | grep -i :%x", internalPort);
33+
command += " || ";
34+
command += format("nc -vz -w 1 localhost %d", internalPort);
35+
command += " || ";
36+
command += format("/bin/bash -c '</dev/tcp/localhost/%d'", internalPort);
37+
command += ")";
2438
}
2539

26-
return true;
27-
}
28-
29-
private void tryPort(Integer internalPort) {
30-
String[][] commands = {
31-
{"/bin/sh", "-c", format("cat /proc/net/tcp{,6} | awk '{print $2}' | grep -i :%x", internalPort)},
32-
{"/bin/sh", "-c", format("nc -vz -w 1 localhost %d", internalPort)},
33-
{"/bin/bash", "-c", format("</dev/tcp/localhost/%d", internalPort)}
34-
};
35-
36-
for (String[] command : commands) {
37-
try {
38-
if (ExecInContainerPattern.execInContainer(waitStrategyTarget.getContainerInfo(), command).getExitCode() == 0) {
39-
return;
40-
}
41-
} catch (Exception e) {
42-
throw new IllegalStateException(e);
43-
}
40+
Instant before = Instant.now();
41+
try {
42+
ExecResult result = ExecInContainerPattern.execInContainer(waitStrategyTarget.getContainerInfo(), "/bin/sh", "-c", command);
43+
log.trace("Check for {} took {}", internalPorts, Duration.between(before, Instant.now()));
44+
return result.getExitCode() == 0;
45+
} catch (Exception e) {
46+
throw new IllegalStateException(e);
4447
}
45-
46-
throw new IllegalStateException("Socket not listening yet: " + internalPort);
4748
}
4849
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ public class HostPortWaitStrategy extends AbstractWaitStrategy {
2525
protected void waitUntilReady() {
2626
final Set<Integer> externalLivenessCheckPorts = getLivenessCheckPorts();
2727
if (externalLivenessCheckPorts.isEmpty()) {
28-
log.debug("Liveness check ports of {} is empty. Not waiting.", waitStrategyTarget.getContainerInfo().getName());
28+
if (log.isDebugEnabled()) {
29+
log.debug("Liveness check ports of {} is empty. Not waiting.", waitStrategyTarget.getContainerInfo().getName());
30+
}
2931
return;
3032
}
3133

core/src/test/java/org/testcontainers/containers/wait/internal/InternalCommandPortListeningCheckTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import org.testcontainers.containers.BindMode;
77
import org.testcontainers.containers.GenericContainer;
88

9-
import static org.rnorth.visibleassertions.VisibleAssertions.assertThrows;
9+
import static org.rnorth.visibleassertions.VisibleAssertions.assertFalse;
1010
import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;
1111

1212
public class InternalCommandPortListeningCheckTest {
@@ -30,8 +30,8 @@ public void singleListening() {
3030
public void nonListening() {
3131
final InternalCommandPortListeningCheck check = new InternalCommandPortListeningCheck(nginx, ImmutableSet.of(8080, 1234));
3232

33-
assertThrows("InternalCommandPortListeningCheck detects a non-listening port among many",
34-
IllegalStateException.class,
35-
(Runnable) check::call);
33+
final Boolean result = check.call();
34+
35+
assertFalse("InternalCommandPortListeningCheck detects a non-listening port among many", result);
3636
}
37-
}
37+
}

0 commit comments

Comments
 (0)