[Daemon] Improve daemon connection algorithm

Before this change, the logic was to find the most suitable daemon and try connecting to it in a loop, ignoring the fact that it could be dying. Now, instead of trying to connect to the daemon dying daemon in a loop, we will make only 1 attemp and then skip if it's already dying.
^KT-55322 In Progress
This commit is contained in:
Alexander.Likhachev
2023-11-21 09:35:37 +01:00
committed by Space Team
parent 44d6807aba
commit 8ceee3e8b9
4 changed files with 52 additions and 45 deletions
+1 -13
View File
@@ -1,6 +1,3 @@
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
description = "Kotlin Daemon Client" description = "Kotlin Daemon Client"
plugins { plugins {
@@ -32,16 +29,7 @@ dependencies {
} }
} }
tasks.withType<KotlinCompilationTask<*>> { configureKotlinCompileTasksGradleCompatibility()
compilerOptions {
// This module is being run from within Gradle, older versions of which only have older kotlin-stdlib in the runtime classpath.
@Suppress("DEPRECATION")
apiVersion.set(KotlinVersion.KOTLIN_1_4)
@Suppress("DEPRECATION")
languageVersion.set(KotlinVersion.KOTLIN_1_4)
freeCompilerArgs.add("-Xsuppress-version-warnings")
}
}
sourceSets { sourceSets {
"main" { projectDefault() } "main" { projectDefault() }
@@ -101,36 +101,45 @@ object KotlinCompilerClient {
autostart: Boolean, autostart: Boolean,
leaseSession: Boolean, leaseSession: Boolean,
sessionAliveFlagFile: File? = null, sessionAliveFlagFile: File? = null,
): CompileServiceSession? = connectLoop(reportingTargets, autostart) { isLastAttempt -> ): CompileServiceSession? {
val ignoredDaemonSessionFiles = mutableSetOf<File>()
return connectLoop(reportingTargets, autostart) { isLastAttempt ->
fun CompileService.leaseImpl(): CompileServiceSession? { fun CompileService.tryToLeaseSession(): CompileServiceSession? {
// the newJVMOptions could be checked here for additional parameters, if needed // the newJVMOptions could be checked here for additional parameters, if needed
registerClient(clientAliveFlagFile.absolutePath) registerClient(clientAliveFlagFile.absolutePath)
reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon") reportingTargets.report(DaemonReportCategory.DEBUG, "connected to the daemon")
if (!leaseSession) return CompileServiceSession(this, CompileService.NO_SESSION) if (!leaseSession) return CompileServiceSession(this, CompileService.NO_SESSION)
return leaseCompileSession(sessionAliveFlagFile?.absolutePath).takeUnless { it is CompileService.CallResult.Dying }?.let { return leaseCompileSession(sessionAliveFlagFile?.absolutePath).takeUnless { it is CompileService.CallResult.Dying }?.let {
CompileServiceSession(this, it.get()) CompileServiceSession(this, it.get())
} }
} }
ensureServerHostnameIsSetUp() ensureServerHostnameIsSetUp()
val (service, newJVMOptions) = tryFindSuitableDaemonOrNewOpts( val result = tryFindSuitableDaemonOrNewOpts(
File(daemonOptions.runFilesPath), File(daemonOptions.runFilesPath),
compilerId, compilerId,
daemonJVMOptions daemonJVMOptions,
) { cat, msg -> reportingTargets.report(cat, msg) } ignoredDaemonSessionFiles,
) { cat, msg -> reportingTargets.report(cat, msg) }
if (service != null) {
service.leaseImpl() when (result) {
} else { is DaemonSearchResult.Found -> result.compileService.tryToLeaseSession().also {
if (!isLastAttempt && autostart) { // the null value here means that the daemon is already dying,
if (startDaemon(compilerId, newJVMOptions, daemonOptions, reportingTargets)) { // so we shall query other daemons or start a new one
reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it") if (it == null) ignoredDaemonSessionFiles.add(result.runFileMarker)
}
is DaemonSearchResult.NotFound -> {
if (!isLastAttempt && autostart) {
if (startDaemon(compilerId, result.requiredJvmOptions, daemonOptions, reportingTargets)) {
reportingTargets.report(DaemonReportCategory.DEBUG, "new daemon started, trying to find it")
}
}
null
} }
} }
null
} }
} }
@@ -384,16 +393,28 @@ object KotlinCompilerClient {
return null return null
} }
private sealed interface DaemonSearchResult {
class Found(
val compileService: CompileService,
val runFileMarker: File,
) : DaemonSearchResult
class NotFound(
val requiredJvmOptions: DaemonJVMOptions,
) : DaemonSearchResult
}
private fun tryFindSuitableDaemonOrNewOpts( private fun tryFindSuitableDaemonOrNewOpts(
registryDir: File, registryDir: File,
compilerId: CompilerId, compilerId: CompilerId,
daemonJVMOptions: DaemonJVMOptions, daemonJVMOptions: DaemonJVMOptions,
ignoredDaemonSessionFiles: Set<File>,
report: (DaemonReportCategory, String) -> Unit, report: (DaemonReportCategory, String) -> Unit,
): Pair<CompileService?, DaemonJVMOptions> { ): DaemonSearchResult {
registryDir.mkdirs() registryDir.mkdirs()
val timestampMarker = Files.createTempFile(registryDir.toPath(), "kotlin-daemon-client-tsmarker", null).toFile() val timestampMarker = Files.createTempFile(registryDir.toPath(), "kotlin-daemon-client-tsmarker", null).toFile()
val aliveWithMetadata = try { val aliveWithMetadata = try {
walkDaemons(registryDir, compilerId, timestampMarker, report = report).toList() walkDaemons(registryDir, compilerId, timestampMarker, report = report, filter = { file, _ -> file !in ignoredDaemonSessionFiles }).toList()
} finally { } finally {
timestampMarker.delete() timestampMarker.delete()
} }
@@ -403,10 +424,10 @@ object KotlinCompilerClient {
val optsCopy = daemonJVMOptions.copy() val optsCopy = daemonJVMOptions.copy()
// if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon // if required options fit into fattest running daemon - return the daemon and required options with memory params set to actual ones in the daemon
return aliveWithMetadata.maxWithOrNull(comparator)?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }?.let { return aliveWithMetadata.maxWithOrNull(comparator)?.takeIf { daemonJVMOptions memorywiseFitsInto it.jvmOptions }?.let {
Pair(it.daemon, optsCopy.updateMemoryUpperBounds(it.jvmOptions)) DaemonSearchResult.Found(it.daemon, it.runFile)
} }
// else combine all options from running daemon to get fattest option for a new daemon to run // else combine all options from running daemon to get fattest option for a new daemon to run
?: Pair(null, aliveWithMetadata.fold(optsCopy) { opts, d -> opts.updateMemoryUpperBounds(d.jvmOptions) }) ?: DaemonSearchResult.NotFound(aliveWithMetadata.fold(optsCopy) { opts, d -> opts.updateMemoryUpperBounds(d.jvmOptions) })
} }
@@ -39,7 +39,7 @@ interface CompileService : Remote {
} }
companion object { companion object {
val NO_SESSION: Int = 0 const val NO_SESSION: Int = 0
} }
sealed class CallResult<out R> : Serializable { sealed class CallResult<out R> : Serializable {
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.daemon
import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient
import org.jetbrains.kotlin.daemon.common.CompileService import org.jetbrains.kotlin.daemon.common.CompileService
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import java.io.File import java.io.File
@@ -36,7 +35,6 @@ class LastSessionDaemonTest : BaseDaemonSessionTest() {
assertEquals(0, exitCode) assertEquals(0, exitCode)
} }
@Disabled("KT-55322")
@DisplayName("can lease a session when the daemon in the LastSession state") // either by starting a new daemon or returning it to the Alive state @DisplayName("can lease a session when the daemon in the LastSession state") // either by starting a new daemon or returning it to the Alive state
@Test @Test
fun canLeaseNewSession() { fun canLeaseNewSession() {