From 46c09fac8666d52db2d7fc227bcfb0b0e4959707 Mon Sep 17 00:00:00 2001 From: cgivre Date: Tue, 8 Sep 2026 10:38:39 -0400 Subject: [PATCH] Shard CI tests and raise the surefire fork count The unit test suite dominates CI wall clock, and exec/java-exec alone accounts for roughly half of it. Split each matrix entry three ways by test class name and run two surefire forks per job. The shard patterns are exhaustive over every test class in the repo, so a newly added test always lands in a shard instead of being silently skipped. --- .github/workflows/ci.yml | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23e4cdeb5b5..f93048c9bd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ on: [push, pull_request] jobs: build: - name: Main Build + name: Main Build (java ${{ matrix.java }}, shard ${{ matrix.shard }}) runs-on: ubuntu-latest if: github.repository == 'apache/drill' timeout-minutes: 150 @@ -36,6 +36,12 @@ jobs: # Java versions to run unit tests (Jetty 12 requires Java 17+) java: [ '17', '21', '25' ] profile: ['default-hadoop'] + # exec/java-exec holds ~31 minutes of the suite in 577 classes, which dominates + # the build. Splitting it three ways by class name brings the longest shard to + # about 11 minutes. Boundaries were chosen from measured per-class times; the + # patterns are exhaustive, so a newly added test always lands in exactly one + # shard rather than being silently skipped. + shard: [ '1', '2', '3' ] fail-fast: false steps: - name: Checkout @@ -57,9 +63,36 @@ jobs: mkswap /tmp/swapfile swapon /tmp/swapfile " + # Which slice of the suite this job runs. The patterns are spelled out one prefix + # at a time because surefire does NOT support character-class globs: + # -Dtest='Test[A-F]*' silently matches nothing and the build passes green having + # run no tests. Only '*' wildcards and comma separated lists work, so each letter + # gets its own entry, in both cases (TestforBaseTestInheritance has a lowercase f). + # + # Shard 3 repeats surefire's other default includes (*Test, *Tests, *TestCase) to + # pick up classes not named Test*. Verified exhaustive against every test + # class in the repo. TestFrameworkTest matches shards 1 and 3 and runs twice — + # harmless, and far safer than a gap that skips tests without saying so. + - name: Select test shard + id: shard + run: | + case "${{ matrix.shard }}" in + 1) echo 'pattern=TestA*,Testa*,TestB*,Testb*,TestC*,Testc*,TestD*,Testd*,TestE*,Teste*,TestF*,Testf*' >> "$GITHUB_OUTPUT" ;; + 2) echo 'pattern=TestG*,Testg*,TestH*,Testh*,TestI*,Testi*,TestJ*,Testj*,TestK*,Testk*,TestL*,Testl*,TestM*,Testm*,TestN*,Testn*,TestO*,Testo*,TestP*,Testp*' >> "$GITHUB_OUTPUT" ;; + 3) echo 'pattern=TestQ*,Testq*,TestR*,Testr*,TestS*,Tests*,TestT*,Testt*,TestU*,Testu*,TestV*,Testv*,TestW*,Testw*,TestX*,Testx*,TestY*,Testy*,TestZ*,Testz*,*Test,*Tests,*TestCase,Test0*,Test1*,Test2*,Test3*,Test4*,Test5*,Test6*,Test7*,Test8*,Test9*,Test_*' >> "$GITHUB_OUTPUT" ;; + esac + + # -DforkCount=2: several of the slowest classes are wall-clock bound rather than + # CPU bound — TestTimedCallable spends 105 seconds asleep, and the RPC, SSL and + # graceful-shutdown tests wait on timeouts — so a second fork overlaps that idle + # time at little extra cost. Raise further only with memory headroom: the vector + # tests fail with OutOfMemoryException under pressure. - name: Build and test run: | mvn -P${{ matrix.profile }} install --batch-mode --no-transfer-progress \ + -DforkCount=2 \ + -Dsurefire.failIfNoSpecifiedTests=false \ + -Dtest='${{ steps.shard.outputs.pattern }}' \ -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120 - name: Remove swap space run : |