Skip to content

Commit acc377b

Browse files
Simplify launchOrConnectToServer to eliminate nested retries (#5872)
Previously we needed a `retryWithTimeout` around the reading of the port because the initial process launch could take >10s, which would make the outer `retryWithTimeout` not take effect. This is because we were performing the coursier runner classpath resolution as part of that launch, which can take arbitrarily long, overshooting the retry timeout. This PR fixes it by moving the coursier runner classpath resolution out of the retry loop, so the retry loop only contains the logic around launching the process. This should help keep the time of each attempt to a more reasonable amount, allowing the outer retries to work and avoiding the need for nested retries around the individual steps of the process launch workflow --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 0979a6e commit acc377b

File tree

9 files changed

+54
-33
lines changed

9 files changed

+54
-33
lines changed

integration/failure/invalid-build-header-no-space/src/InvalidBuildHeaderNoSpaceTests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import mill.testkit.UtestIntegrationTestSuite
55
import utest._
66

77
object InvalidBuildHeaderNoSpaceTests extends UtestIntegrationTestSuite {
8+
override def cleanupProcessIdFile =
9+
false // process never launches due to yaml header syntax error
810
val tests: Tests = Tests {
911
test - integrationTest { tester =>
1012
import tester._

integration/failure/invalid-build-header/src/InvalidBuildHeaderTests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import mill.testkit.UtestIntegrationTestSuite
55
import utest._
66

77
object InvalidBuildHeaderTests extends UtestIntegrationTestSuite {
8+
override def cleanupProcessIdFile =
9+
false // process never launches due to yaml header syntax error
810
val tests: Tests = Tests {
911
test - integrationTest { tester =>
1012
import tester._

libs/daemon/client/src/mill/client/ServerLauncher.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -142,23 +142,21 @@ public static Launched launchOrConnectToServer(
142142
try (var ignored = locks.launcherLock.lock()) {
143143
return retryWithTimeout(serverInitWaitMillis, "server launch failed", () -> {
144144
try {
145+
log.accept("launchOrConnectToServer attempt");
146+
145147
var result =
146148
ensureServerIsRunning(locks, daemonDir, initServer, serverInitWaitMillis / 3, log);
147149
if (result instanceof ServerLaunchResult.Success
148150
|| result instanceof ServerLaunchResult.AlreadyRunning) {
149151
log.accept("Reading server port: " + daemonDir.toAbsolutePath());
150-
var port = retryWithTimeout(serverInitWaitMillis / 10, "Reading server port", () -> {
151-
try {
152-
return Optional.of(
153-
Integer.parseInt(Files.readString(daemonDir.resolve(DaemonFiles.socketPort))));
154-
} catch (IOException e) {
155-
throw new RuntimeException(e);
156-
}
157-
});
152+
var port =
153+
Integer.parseInt(Files.readString(daemonDir.resolve(DaemonFiles.socketPort)));
154+
158155
var launched = new Launched();
159156
launched.port = port;
160-
log.accept("Read server port, connecting: " + port);
157+
log.accept("Read server port");
161158
if (openSocket) {
159+
log.accept("Connecting: " + port);
162160
var connected = new Socket(InetAddress.getLoopbackAddress(), port);
163161
launched.socket = connected;
164162
}
@@ -276,6 +274,7 @@ public void kill() {
276274
};
277275
return Optional.of(new ServerLaunchResult.AlreadyRunning(launchedServer));
278276
} catch (IOException | NumberFormatException e) {
277+
log.accept("Read PID exception: " + e);
279278
return Optional.empty();
280279
}
281280
}

runner/launcher/src/mill/launcher/MillLauncherMain.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package mill.launcher;
22

3+
import java.nio.file.Files;
34
import java.nio.file.Path;
45
import java.nio.file.Paths;
6+
import java.time.Instant;
7+
import java.time.ZoneId;
8+
import java.time.format.DateTimeFormatter;
59
import java.util.Arrays;
610
import java.util.Collections;
711
import java.util.Optional;
812
import java.util.function.Consumer;
913
import mill.client.*;
1014
import mill.client.lock.Locks;
15+
import mill.constants.BuildInfo;
1116
import mill.constants.EnvVars;
1217
import mill.constants.OutFiles;
1318
import mill.constants.OutFolderMode;
@@ -70,16 +75,30 @@ public static void main(String[] args) throws Exception {
7075
+ " make it less responsive.");
7176
}
7277

78+
String[] runnerClasspath = MillProcessLauncher.cachedComputedValue0(
79+
outMode,
80+
"resolve-runner",
81+
BuildInfo.millVersion,
82+
() -> CoursierClient.resolveMillDaemon(),
83+
arr -> {
84+
for (String s : arr) {
85+
if (!Files.exists(Paths.get(s))) return false;
86+
}
87+
return true;
88+
});
89+
7390
if (runNoDaemon) {
7491
// start in no-server mode
75-
System.exit(MillProcessLauncher.launchMillNoDaemon(args, outMode));
92+
System.exit(MillProcessLauncher.launchMillNoDaemon(args, outMode, runnerClasspath));
7693
} else {
7794
var logs = new java.util.ArrayList<String>();
7895
try {
7996
// start in client-server mode
8097
var optsArgs = new java.util.ArrayList<>(MillProcessLauncher.millOpts(outMode));
8198
Collections.addAll(optsArgs, args);
82-
99+
var formatter =
100+
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'").withZone(ZoneId.of("UTC"));
101+
Consumer<String> log = (s) -> logs.add(formatter.format(Instant.now()) + " " + s);
83102
MillServerLauncher launcher =
84103
new MillServerLauncher(
85104
new MillServerLauncher.Streams(System.in, System.out, System.err),
@@ -89,13 +108,14 @@ public static void main(String[] args) throws Exception {
89108
-1) {
90109
public LaunchedServer initServer(Path daemonDir, Locks locks) throws Exception {
91110
return new LaunchedServer.OsProcess(
92-
MillProcessLauncher.launchMillDaemon(daemonDir, outMode).toHandle());
111+
MillProcessLauncher.launchMillDaemon(daemonDir, outMode, runnerClasspath)
112+
.toHandle());
93113
}
94114
};
95115

96116
var daemonDir0 = Paths.get(outDir, OutFiles.millDaemon);
97117
String javaHome = MillProcessLauncher.javaHome(outMode);
98-
Consumer<String> log = logs::add;
118+
99119
var exitCode = launcher.run(daemonDir0, javaHome, log).exitCode;
100120
if (exitCode == ClientUtil.ExitServerCodeWhenVersionMismatch()) {
101121
exitCode = launcher.run(daemonDir0, javaHome, log).exitCode;

runner/launcher/src/mill/launcher/MillProcessLauncher.java

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919

2020
public class MillProcessLauncher {
2121

22-
static int launchMillNoDaemon(String[] args, OutFolderMode outMode) throws Exception {
22+
static int launchMillNoDaemon(String[] args, OutFolderMode outMode, String[] runnerClasspath)
23+
throws Exception {
2324
final String sig = String.format("%08x", UUID.randomUUID().hashCode());
2425
final Path processDir =
2526
Paths.get(".").resolve(outFor(outMode)).resolve(millNoDaemon).resolve(sig);
2627

2728
final List<String> l = new ArrayList<>();
28-
l.addAll(millLaunchJvmCommand(outMode));
29+
l.addAll(millLaunchJvmCommand(outMode, runnerClasspath));
2930
Map<String, String> propsMap = ClientUtil.getUserSetProperties();
3031
for (String key : propsMap.keySet()) l.add("-D" + key + "=" + propsMap.get(key));
3132
l.add("mill.daemon.MillNoDaemonMain");
@@ -56,8 +57,9 @@ static int launchMillNoDaemon(String[] args, OutFolderMode outMode) throws Excep
5657
}
5758
}
5859

59-
static Process launchMillDaemon(Path daemonDir, OutFolderMode outMode) throws Exception {
60-
List<String> l = new ArrayList<>(millLaunchJvmCommand(outMode));
60+
static Process launchMillDaemon(Path daemonDir, OutFolderMode outMode, String[] runnerClasspath)
61+
throws Exception {
62+
List<String> l = new ArrayList<>(millLaunchJvmCommand(outMode, runnerClasspath));
6163
l.add("mill.daemon.MillDaemonMain");
6264
l.add(daemonDir.toFile().getCanonicalPath());
6365
l.add(outMode.asString());
@@ -212,7 +214,8 @@ static String javaExe(OutFolderMode outMode) throws Exception {
212214
}
213215
}
214216

215-
static List<String> millLaunchJvmCommand(OutFolderMode outMode) throws Exception {
217+
static List<String> millLaunchJvmCommand(OutFolderMode outMode, String[] runnerClasspath)
218+
throws Exception {
216219
final List<String> vmOptions = new ArrayList<>();
217220

218221
// Java executable
@@ -234,17 +237,7 @@ static List<String> millLaunchJvmCommand(OutFolderMode outMode) throws Exception
234237

235238
vmOptions.add("-XX:+HeapDumpOnOutOfMemoryError");
236239
vmOptions.add("-cp");
237-
String[] runnerClasspath = cachedComputedValue0(
238-
outMode,
239-
"resolve-runner",
240-
BuildInfo.millVersion,
241-
() -> CoursierClient.resolveMillDaemon(),
242-
arr -> {
243-
for (String s : arr) {
244-
if (!Files.exists(Paths.get(s))) return false;
245-
}
246-
return true;
247-
});
240+
248241
vmOptions.add(String.join(File.pathSeparator, runnerClasspath));
249242

250243
return vmOptions;

testkit/src/mill/testkit/ExampleTester.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ class ExampleTester(
7676
millExecutable: os.Path,
7777
bashExecutable: String = ExampleTester.defaultBashExecutable(),
7878
val baseWorkspacePath: os.Path,
79-
val propagateJavaHome: Boolean = true
79+
val propagateJavaHome: Boolean = true,
80+
val cleanupProcessIdFile: Boolean = true
8081
) extends IntegrationTesterBase {
8182

8283
def processCommandBlock(commandBlock: String): Unit = {

testkit/src/mill/testkit/IntegrationTestSuite.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ trait IntegrationTestSuite {
3535
*/
3636
protected def propagateJavaHome: Boolean = true
3737

38+
protected def cleanupProcessIdFile: Boolean = true
3839
def debugLog: Boolean = false
3940

4041
/**
@@ -53,7 +54,8 @@ trait IntegrationTestSuite {
5354
millExecutable,
5455
debugLog,
5556
baseWorkspacePath = os.pwd,
56-
propagateJavaHome = propagateJavaHome
57+
propagateJavaHome = propagateJavaHome,
58+
cleanupProcessIdFile = cleanupProcessIdFile
5759
)
5860
try block(tester)
5961
finally tester.close()

testkit/src/mill/testkit/IntegrationTester.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class IntegrationTester(
2626
val millExecutable: os.Path,
2727
override val debugLog: Boolean = false,
2828
val baseWorkspacePath: os.Path = os.pwd,
29-
val propagateJavaHome: Boolean = true
29+
val propagateJavaHome: Boolean = true,
30+
val cleanupProcessIdFile: Boolean = true
3031
) extends IntegrationTester.Impl {
3132
initWorkspace()
3233
}

testkit/src/mill/testkit/IntegrationTesterBase.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import mill.util.Retry
66
trait IntegrationTesterBase {
77
def workspaceSourcePath: os.Path
88
def daemonMode: Boolean
9+
def cleanupProcessIdFile: Boolean
910

1011
def propagateJavaHome: Boolean
1112

@@ -75,7 +76,7 @@ trait IntegrationTesterBase {
7576
def removeProcessIdFile(): Unit = {
7677
if (!sys.env.contains("MILL_TEST_SHARED_OUTPUT_DIR")) {
7778
val outDir = os.Path(out, workspacePath)
78-
if (os.exists(outDir)) {
79+
if (os.exists(outDir) && cleanupProcessIdFile) {
7980
if (daemonMode) {
8081
val serverPath = outDir / millDaemon
8182
os.remove(serverPath / processId)

0 commit comments

Comments
 (0)