Uh oh!
There was an error while loading. Please reload this page.
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.
Uh oh!
There was an error while loading. Please reload this page.
[ci: NON_DEFAULT_JVMS]
[ci: NON_DEFAULTS_JVM]
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
AlexeyKuznetsov-DD
commented
May 29, 2026
/merge --commit-message "Cleanup Gradle build daemon after Launcher tests" |
View all feedbacks in Devflow UI.
The expected merge time in
|
194ee63
into
masterUh oh!
There was an error while loading. Please reload this page.
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]