Cleanup Gradle build daemon after Launcher tests#11499
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 136bb6ee87
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| env.put("GRADLE_USER_HOME", gradleUserHome.toString()); | ||
| env.put("GRADLE_OPTS", ""); | ||
| ShellCommandExecutor shellCommandExecutor = | ||
| new ShellCommandExecutor(projectFolder.toFile(), GRADLE_STOP_TIMEOUT_MILLIS, env); |
There was a problem hiding this comment.
Use an existing project directory for daemon shutdown
Because projectFolder is an instance @TempDir from AbstractGradleTest, JUnit scopes it to each parameterized invocation and cleans it up when that invocation finishes; by the time this @AfterAll runs, the last projectFolder has already been deleted. In that state ShellCommandExecutor starts ./gradlew --stop with a non-existent working directory, the exception is swallowed by the best-effort catch, and the shared static gradleUserHome is still left for JUnit to delete while Gradle daemons may hold files open.
Useful? React with 👍 / 👎.
🟢 Java Benchmark SLOs — All performance SLOs passed
PR vs. master results
Commit: Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion. |
AlexeyKuznetsov-DD
left a comment
There was a problem hiding this comment.
- Let's test on CI with [NON_DEFAULT_JVMS=true].
- Change PR description
AfterAll->AfrerEach.
This comment has been minimized.
This comment has been minimized.
[ci: NON_DEFAULT_JVMS]
[ci: NON_DEFAULTS_JVM]
|
/merge --commit-message "Cleanup Gradle build daemon after Launcher tests" |
|
View all feedbacks in Devflow UI.
The expected merge time in
|
194ee63
into
master
What Does This Do
Makes the Gradle smoke tests robust against intermittent CI failures, with three related changes:
GradleLauncherSmokeTestteardown flake: adds an@AfterEachhook that runs./gradlew --stopto stop the Gradle build daemon before JUnit deletes the static@TempDir gradleUserHome.GradleDaemonSmokeTestteardown flake: makes the static@TempDir testKitFoldercleanup deterministic. An@AfterAllhook stops the TestKit daemons before the directory is deleted —DefaultGradleConnector.close()(graceful) followed by killing any surviving daemon by PID — and the directory is then deleted on a best-effort basis (@TempDir(cleanup = CleanupMode.NEVER)+ a retrying quiet delete) so cleanup can never turn into a class-level failure. The kill + quiet-delete helpers live inAbstractGradleTest.succeed-gradle-plugin-testOutOfMemoryError: bumps the shared smoke-test build/daemon heap from-Xmx256mto-Xmx512minCiVisibilitySmokeTest.Motivation
GradleLauncherSmokeTest
GradleLauncherSmokeTestwas flaky in CI. The test itself always passed, but teardown intermittently failed with:Even though the launcher runs with
--no-daemon, Gradle still spawns a single-use build daemon that writes into$GRADLE_USER_HOME/daemon/<version>/. When that process hasn't fully released its file handles by the time JUnit's static@TempDircleanup runs, the recursive delete races against it and fails withDirectoryNotEmptyException, surfacing as a class-level failure.Explicitly stopping the daemon in
@AfterEach(which runs before the static@TempDircleanup) makes the deletion deterministic.GradleDaemonSmokeTest
The TestKit-based
GradleDaemonSmokeTestsuffers from the same class of teardown failure:TestKit runs every build in a daemon rooted under
<testKitFolder>/test-kit-daemonwith a 120s idle timeout. At class teardown a daemon is often still alive and still holding file handles on itscaches/<version>directory (e.g.caches/8.9for thesucceed-new-8.9scenario), so JUnit's recursive delete of the static@TempDir testKitFolderfails.This failure mode is not new behavior — it only began surfacing after the test was migrated from Spock to JUnit 5 (#11439). Spock's
@TempDirdeletes best-effort and silently leaks an undeletable directory; JUnit 5's@TempDirtreats a failed recursive delete as a hard error (Failed to delete temp directory→executionError). The same lingering-daemon race that Spock tolerated now fails the build.The Tooling API used by
GradleRunnerprovides no public way to stop the daemons it spawns (gradle/gradle#12535), andDefaultGradleConnector.close()does not reliably terminate them — soclose()alone (graceful-first) is not enough. The teardown therefore also force-kills any surviving daemon by parsing its PID from thedaemon-<pid>.out.logfilename and sendingkill/taskkill(mirroring Gradle's ownDaemonLogsAnalyzer.killAll()), which releases the cache handles. Finally, the directory is deleted by the test rather than by JUnit (CleanupMode.NEVER+ a short retrying quiet delete), so that even if a handle outlives the kill the result is a leaked temp dir (reaped by the OS) plus a warning — never a failed run.OutOfMemoryError in succeed-gradle-plugin-test
The
test-succeed-gradle-plugin-testproject appliesjava-gradle-plugin, which putsgradleApi()on its compile classpath. This could overflow the 256m daemon heap, making the build die with:Additional Notes
test-environment-trigger: skip
Contributor Checklist
type:and (comp:orinst:) labels in addition to any other useful labelsclose,fix, or any linking keywords when referencing an issueUse
solvesinstead, and assign the PR milestone to the issue/merge. You can also:/merge --commit-message "..."/merge -c/merge -f --reason "reason"; please use this judiciously, as some checks do not run at the PR-levelJira ticket: [PROJ-IDENT]