This is an automated email from the ASF dual-hosted git repository.

yuqi1129 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new a80f6fd2e7 [#12583] improvement(ci): Optimize build test execution 
(#12584)
a80f6fd2e7 is described below

commit a80f6fd2e70c5d9e76ed98b3fbae706533687740
Author: Qi Yu <[email protected]>
AuthorDate: Fri Aug 28 11:44:42 2026 +0800

    [#12583] improvement(ci): Optimize build test execution (#12584)
    
    ### What changes were proposed in this pull request?
    
    - Write each Gradle test report to a task-specific directory so
    independent test tasks can run in parallel.
    - Serialize projects containing Docker-tagged tests through a shared
    Gradle build service because they share containers and other test
    resources.
    - Limit the build job to two Gradle workers to avoid CPU and memory
    contention between test JVMs.
    - Raise the build job timeout from 90 to 120 minutes.
    
    ### Why are the changes needed?
    
    The build job frequently approaches or exceeds its 90-minute timeout.
    Gradle parallel execution is enabled, but the shared test report
    directory forces all test tasks to run one at a time. Docker-based tests
    cannot all be made parallel because several projects share servers,
    databases, containers, and configuration files.
    
    ### Measured result
    
    The serial baseline ([run
    
32871678601](https://github.com/apache/gravitino/actions/runs/32871678601/job/97880344037))
    executed 62 test tasks with a maximum concurrency of one:
    
    | Metric | Serial baseline |
    |---|---:|
    | Sum of test task durations | 68.43 min |
    | First test start to last test end | 69.65 min |
    | `Build with Gradle` | 70.03 min |
    | Complete build job | 78.33 min |
    
    Three successful revisions of this PR with `--max-workers=2` produced
    consistent results ([run
    
32729975754](https://github.com/apache/gravitino/actions/runs/32729975754/job/97439835322),
    [run
    
32823784437](https://github.com/apache/gravitino/actions/runs/32823784437/job/97727370085),
    and [run
    
32833427151](https://github.com/apache/gravitino/actions/runs/32833427151/job/97769263031)):
    
    | Metric | Two workers |
    |---|---:|
    | Maximum concurrent test tasks | 2 |
    | Sum of test task durations | 70.19–70.96 min |
    | First test start to last test end | 63.04–63.79 min |
    | `Build with Gradle` | 63.43–64.13 min |
    | Complete build job | 72.52–76.58 min |
    
    This reduces the test and Gradle critical path by about six minutes, or
    8–9%. Complete job time varies more because `publishToMavenLocal` alone
    varied from about 5 to 12 minutes across these runs.
    
    An experiment without the worker limit ([run
    
32943743979](https://github.com/apache/gravitino/actions/runs/32943743979/job/98100012872))
    allowed four concurrent test tasks, but increased their summed duration
    to 82.07 minutes and the test wall time to 68.32 minutes. The Gradle
    step regressed to 68.83 minutes because resource contention offset most
    of the additional parallelism. The two-worker limit is therefore
    retained.
    
    ### Remaining bottleneck
    
    Twenty-two test tasks must still hold the shared Docker lock and account
    for roughly 61–62 minutes with two workers. `:core:test` is the largest
    part of that chain. A material reduction beyond this PR requires
    sharding the Docker-heavy tests across separate runners and aggregating
    their coverage reports.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    - Verified from three CI runs that independent test tasks reach a
    concurrency of two while Docker-locked tasks remain serialized.
    - Verified the uncapped run reaches four concurrent test tasks and
    regresses due to resource contention.
    - Ran `gradle help --max-workers=2 -PskipWeb=true` successfully with
    proxies disabled.
    - Ran `git diff --check`.
---
 .github/workflows/build.yml |  3 ++-
 build.gradle.kts            | 33 ++++++++++++++++++++++++++++++++-
 2 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e7f9117c22..2c3f5a5426 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -152,7 +152,7 @@ jobs:
     strategy:
       matrix:
         java-version: [ 17 ]
-    timeout-minutes: 90
+    timeout-minutes: 120
     needs: changes
     if: needs.changes.outputs.source_changes == 'true'
     # Steps represent a sequence of tasks that will be executed as part of the 
job
@@ -192,6 +192,7 @@ jobs:
 
           gradle_args=(
             build
+            --max-workers=2
             -PskipWeb=true
             -PskipITs
             -PskipDockerTests=false
diff --git a/build.gradle.kts b/build.gradle.kts
index 94ca8ac447..e1d288f139 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -25,6 +25,8 @@ import com.github.jk1.license.render.ReportRenderer
 import com.github.vlsi.gradle.dsl.configureEach
 import net.ltgt.gradle.errorprone.errorprone
 import org.gradle.api.attributes.java.TargetJvmVersion
+import org.gradle.api.services.BuildService
+import org.gradle.api.services.BuildServiceParameters
 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
 import org.gradle.internal.hash.ChecksumService
 import org.gradle.internal.os.OperatingSystem
@@ -34,6 +36,8 @@ import java.util.Locale
 
 Locale.setDefault(Locale.US)
 
+abstract class SharedTestEnvironmentLock : 
BuildService<BuildServiceParameters.None>
+
 plugins {
   `maven-publish`
   id("java")
@@ -61,6 +65,13 @@ plugins {
   alias(libs.plugins.errorprone)
 }
 
+val sharedTestEnvironmentLock = gradle.sharedServices.registerIfAbsent(
+  "sharedTestEnvironmentLock",
+  SharedTestEnvironmentLock::class
+) {
+  maxParallelUsages.set(1)
+}
+
 val snappyJavaVersion: String = libs.versions.snappy.java.get()
 
 val scalaVersion: String = project.properties["scalaVersion"] as? String ?: 
extra["defaultScalaVersion"].toString()
@@ -677,7 +688,11 @@ subprojects {
       showCauses = true
       showStackTraces = true
     }
-    
reports.html.outputLocation.set(file("${rootProject.projectDir}/build/reports/"))
+    // Distinct outputs let Gradle execute independent test tasks in parallel.
+    val testReportPath = path.removePrefix(":").replace(':', '/')
+    reports.html.outputLocation.set(
+      rootProject.layout.buildDirectory.dir("reports/tests/$testReportPath")
+    )
     val skipTests = project.hasProperty("skipTests")
     if (!skipTests) {
       val extraArgs = project.property("extraJvmArgs") as List<String>
@@ -1253,6 +1268,22 @@ gradle.projectsEvaluated {
     subprojectJarOutputDirs.map { 
it.get().asFile.toPath().toAbsolutePath().normalize() }
 
   allprojects {
+    val runsIntegrationTestsOnly = rootProject.hasProperty("skipTests")
+    val hasDockerTests =
+      rootProject.extra["dockerTest"] == true && fileTree("src/test") {
+        include("**/*.java", "**/*.kt")
+      }.any { it.readText().contains("gravitino-docker-test") }
+
+    // Integration tests in different projects share the same Gravitino 
server, database,
+    // containers, and configuration files. Running them together lets one 
test stop or reset
+    // resources while another test is still using them. Normal builds keep 
independent unit tests
+    // parallel and serialize only projects that contain Docker-tagged tests.
+    if (runsIntegrationTestsOnly || hasDockerTests) {
+      tasks.withType<Test>().configureEach {
+        usesService(sharedTestEnvironmentLock)
+      }
+    }
+
     tasks.withType<Jar>().configureEach {
       mustRunAfter(cleanDistributionPackageTask)
     }

Reply via email to