Skip to content

Commit aff0473

Browse files
Avoid preferences lock file contention for tests due to spawned child processes (#10083)
* fix: Try using a test task specific folder for user preference to avoid locks * fix: Use a temporary preference folder for gradle smoke tests * chore: Emit a stacktrace for gradle jobs * chore: Patch smoke test forked processes to use a custom preference file * fix: Don't use the the shared tempdir as it is shared for every test * fix: Previous code, was generating a single gradle properties file per test. Co-authored-by: Alexey Kuznetsov <[email protected]>
1 parent 958e884 commit aff0473

File tree

5 files changed

+66
-8
lines changed

5 files changed

+66
-8
lines changed

buildSrc/src/main/kotlin/dd-trace-java.configure-tests.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ tasks.withType<Test>().configureEach {
4242
!rootProject.providers.gradleProperty("rerun.tests.${project.name}").isPresent
4343
}
4444

45+
// Trick to avoid on CI: "Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock."
46+
// Use a task-specific user prefs directory
47+
systemProperty("java.util.prefs.userRoot", "$buildDir/tmp/userPrefs/${name}")
48+
4549
// Split up tests that want to run forked in their own separate JVM for generated tasks
4650
if (name.startsWith("forkedTest") || name.endsWith("ForkedTest")) {
4751
setExcludes(emptyList())

dd-java-agent/agent-ci-visibility/civisibility-test-fixtures/src/main/groovy/datadog/trace/civisibility/CiVisibilitySmokeTest.groovy

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@ import datadog.trace.api.config.CiVisibilityConfig
66
import datadog.trace.api.config.GeneralConfig
77
import datadog.trace.api.config.TraceInstrumentationConfig
88
import datadog.trace.api.config.TracerConfig
9+
import java.nio.file.Paths
910
import spock.lang.Specification
11+
import spock.lang.TempDir
1012
import spock.util.environment.Jvm
1113

14+
import java.nio.file.Path
15+
1216
import static datadog.trace.util.ConfigStrings.propertyNameToSystemPropertyName
1317

1418
abstract class CiVisibilitySmokeTest extends Specification {
@@ -21,6 +25,9 @@ abstract class CiVisibilitySmokeTest extends Specification {
2125

2226
private static final Map<String,String> DEFAULT_TRACER_CONFIG = defaultJvmArguments()
2327

28+
@TempDir
29+
protected Path prefsDir
30+
2431
protected static String buildJavaHome() {
2532
if (Jvm.current.isJava8()) {
2633
return System.getenv("JAVA_8_HOME")
@@ -69,6 +76,9 @@ abstract class CiVisibilitySmokeTest extends Specification {
6976

7077
protected List<String> buildJvmArguments(String mockBackendIntakeUrl, String serviceName, Map<String, String> additionalArgs) {
7178
List<String> arguments = []
79+
80+
arguments += preventJulPrefsFileLock()
81+
7282
Map<String, String> argMap = buildJvmArgMap(mockBackendIntakeUrl, serviceName, additionalArgs)
7383

7484
// for convenience when debugging locally
@@ -85,6 +95,28 @@ abstract class CiVisibilitySmokeTest extends Specification {
8595
return arguments
8696
}
8797

98+
/**
99+
* Trick to prevent jul Prefs file lock issue on forked processes, in particular in CI which
100+
* runs on Linux and have competing processes trying to write to it, including the Gradle daemon.
101+
*
102+
* <pre><code>
103+
* Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock.
104+
* </code></pre>
105+
*
106+
* Note, some tests can setup arguments on spec level, so `prefsDir` will be `null` during
107+
* `setupSpec()`.
108+
*/
109+
protected String preventJulPrefsFileLock() {
110+
String prefsPath = (prefsDir ?: tempUserPrefsPath()).toAbsolutePath()
111+
return "-Djava.util.prefs.userRoot=$prefsPath".toString()
112+
}
113+
114+
private static Path tempUserPrefsPath() {
115+
String uniqueId = "${System.currentTimeMillis()}_${System.nanoTime()}_${Thread.currentThread().id}"
116+
Path prefsPath = Paths.get(System.getProperty("java.io.tmpdir"), "gradle-test-userPrefs", uniqueId)
117+
return prefsPath
118+
}
119+
88120
protected verifyEventsAndCoverages(String projectName, String toolchain, String toolchainVersion, List<Map<String, Object>> events, List<Map<String, Object>> coverages, List<String> additionalDynamicTags = []) {
89121
def additionalReplacements = ["content.meta.['test.toolchain']": "$toolchain:$toolchainVersion"]
90122

dd-smoke-tests/debugger-integration-tests/src/test/java/datadog/smoketest/ProcessBuilderHelper.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ public static ProcessBuilder createProcessBuilder(
2828
String mainClassName,
2929
String... params) {
3030

31+
// Trick to prevent jul preferences file lock issue on forked processes, in particular in CI
32+
// which runs on Linux and have competing processes trying to write to it, including the
33+
// Gradle daemon.
34+
//
35+
// Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock.
36+
String prefsDir =
37+
System.getProperty("java.io.tmpdir")
38+
+ File.separator
39+
+ "userPrefs"
40+
+ File.separator
41+
+ mainClassName
42+
+ "_"
43+
+ System.nanoTime();
44+
3145
List<String> baseCommand =
3246
Arrays.asList(
3347
javaPath(),
@@ -37,7 +51,8 @@ public static ProcessBuilder createProcessBuilder(
3751
"-javaagent:" + agentShadowJar(),
3852
"-XX:ErrorFile=/tmp/hs_err_pid%p.log",
3953
"-Ddd.env=smoketest",
40-
"-Ddd.version=99");
54+
"-Ddd.version=99",
55+
"-Djava.util.prefs.userRoot=" + prefsDir);
4156

4257
List<String> command = new ArrayList<>();
4358
command.addAll(baseCommand);

dd-smoke-tests/gradle/src/test/groovy/datadog/smoketest/GradleDaemonSmokeTest.groovy

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ class GradleDaemonSmokeTest extends AbstractGradleTest {
3434
@TempDir
3535
Path testKitFolder
3636

37-
def setupSpec() {
38-
givenGradleProperties()
39-
}
40-
4137
@IgnoreIf(reason = "Jacoco plugin does not work with OpenJ9 in older Gradle versions", value = {
4238
JavaVirtualMachine.isJ9()
4339
})
@@ -79,6 +75,7 @@ class GradleDaemonSmokeTest extends AbstractGradleTest {
7975
def "test junit4 class ordering v#gradleVersion"() {
8076
givenGradleVersionIsCompatibleWithCurrentJvm(gradleVersion)
8177
givenGradleProjectFiles(projectName)
78+
givenGradleProjectProperties()
8279
ensureDependenciesDownloaded(gradleVersion)
8380

8481
mockBackend.givenKnownTests(true)
@@ -124,6 +121,7 @@ class GradleDaemonSmokeTest extends AbstractGradleTest {
124121
givenGradleVersionIsCompatibleWithCurrentJvm(gradleVersion)
125122
givenConfigurationCacheIsCompatibleWithCurrentPlatform(configurationCache)
126123
givenGradleProjectFiles(projectName)
124+
givenGradleProjectProperties()
127125
ensureDependenciesDownloaded(gradleVersion)
128126

129127
mockBackend.givenFlakyRetries(flakyRetries)
@@ -150,7 +148,7 @@ class GradleDaemonSmokeTest extends AbstractGradleTest {
150148
}
151149
}
152150

153-
private void givenGradleProperties() {
151+
private void givenGradleProjectProperties() {
154152
assert new File(AGENT_JAR).isFile()
155153

156154
def ddApiKeyPath = testKitFolder.resolve(".dd.api.key")
@@ -173,7 +171,9 @@ class GradleDaemonSmokeTest extends AbstractGradleTest {
173171
def arguments = buildJvmArguments(mockBackend.intakeUrl, TEST_SERVICE_NAME, additionalArgs)
174172

175173
def gradleProperties = "org.gradle.jvmargs=${arguments.join(" ")}".toString()
176-
Files.write(testKitFolder.resolve("gradle.properties"), gradleProperties.getBytes())
174+
// Write to projectFolder (per-test) instead of testKitFolder (shared), so each
175+
// Gradle daemon gets its own unique preference directory
176+
Files.write(projectFolder.resolve("gradle.properties"), gradleProperties.getBytes())
177177
}
178178

179179
private BuildResult runGradleTests(String gradleVersion, boolean successExpected = true, boolean configurationCache = false) {

dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ abstract class AbstractSmokeTest extends ProcessManager {
188188
def javaProperties() {
189189
def tmpDir = "/tmp"
190190

191+
// Trick to prevent jul preferences file lock issue on forked processes, in particular in CI which
192+
// runs on Linux and have competing processes trying to write to it, including the Gradle daemon.
193+
//
194+
// Couldn't flush user prefs: java.util.prefs.BackingStoreException: Couldn't get file lock.
195+
def prefsDir = "${tmpDir}/userPrefs/${this.getClass().simpleName}_${System.nanoTime()}"
196+
191197
def ret = [
192198
"${getMaxMemoryArgumentForFork()}",
193199
"${getMinMemoryArgumentForFork()}",
@@ -204,7 +210,8 @@ abstract class AbstractSmokeTest extends ProcessManager {
204210
"-Ddd.profiling.ddprof.alloc.enabled=${isDdprofSafe()}",
205211
"-Ddatadog.slf4j.simpleLogger.defaultLogLevel=${logLevel()}",
206212
"-Dorg.slf4j.simpleLogger.defaultLogLevel=${logLevel()}",
207-
"-Ddd.site="
213+
"-Ddd.site=",
214+
"-Djava.util.prefs.userRoot=${prefsDir}"
208215
]
209216
if (inferServiceName()) {
210217
ret += "-Ddd.service.name=${SERVICE_NAME}"

0 commit comments

Comments
 (0)