This is an automated email from the ASF dual-hosted git repository.
zhouyuan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gluten.git
The following commit(s) were added to refs/heads/main by this push:
new 7b193da3fd [GLUTEN-6887][VL] Daily Update Velox Version (2026_10_23)
(#13418)
7b193da3fd is described below
commit 7b193da3fd462db56c0b66a11762d748d145269f
Author: Gluten Performance Bot
<[email protected]>
AuthorDate: Fri Oct 23 17:57:08 2026 +0100
[GLUTEN-6887][VL] Daily Update Velox Version (2026_10_23) (#13418)
* [GLUTEN-6887][VL] Daily Update Velox Version (dft-2026_10_23)
Upstream Velox's New Commits:
72ad30f8f by Maria Basmanova, fix(exec): Support broadcast in FixedPoint
body plans (#19753)
1afcf9079 by Pedro Pedreira, refactor(functions): Dispatch MakeRowFromMap
on the map encoding (#19674)
09bced8ca by GALI PREM SAGAR, feat(cudf): Run from_unixtime and to_unixtime
on the GPU
1831f3590 by Ke, fix(nimble): Free previous chunk before decoding the next
e1cefcc92 by Ke, perf(nimble): Skip value decoding for null filters (#19669)
8d6a4ad19 by Hongze Zhang, fix!: CountDistinctAggregate should override
initializeNewGroupsInternal
cbe4bec92 by inf / Hazmi, feat(iceberg): Add split offsets to commit
metadata
58d01dc99 by Mariam AlMesfer, feat(spark): Add support for
unix_timestamp/to_unix_timestamp(timestamp_utc)
f40aa0c9a by Wechar Yu, fix(spill): Delete spill inputs after successful
pre-merge
f0e1a32fd by Apurva Kumar, fix(iceberg): Omit unknown null counts from DWRF
data file metrics (#19738)
0068a37cb by GALI PREM SAGAR, fix(fuzzer): Keep nulls out of the k-RANGE
bound sent to Presto
07c29ee4e by oerling, feat(torchwave): Faster scatter_add and compiler
fixes (#19679)
b8982c7f6 by dependabot[bot], build(deps): Bump launch-editor from 2.14.1
to 2.14.2 in /website
7ff007df4 by Misiu Godfrey, fix(cudf): Preserve 64-bit offsets when
restoring VARBINARY format
7fbd57fde by Maria Basmanova, fix(exec)!: Fail when a convergence plan
produces no row
d499fb3df by Apurva Kumar, fix(iceberg): Commit a deletion vector under its
data file's partition spec
54c5d740b by tanjialiang, refactor(nimble): Let CachedDataInput run on a
cache other than AsyncDataCache (#19663)
cca087339 by Dmitry Spikhalsky, fix(exec): Clear FixedPointLoop next-row
link with an unaligned store
3f397aff6 by Dmitry Spikhalsky, fix(dwio): Use unaligned loads in
DecoderUtil bit helpers
22ac4a306 by Dmitry Spikhalsky, fix(functions): Use unaligned loads in
Murmur3 32-bit string hashes
4c900b305 by Natasha Sehgal, test(fuzzer): Update padding skip issue
references (#19647)
241ed544d by mohsaka, feat(type): Parse Hive "void" type as Velox UNKNOWN
d645ae590 by Xin Zhang, fix(docs): Fix broken TLSF paper link
34403efaa by Krishna Pai, build(ci): Make stash saves and restores best
effort
b4957001e by Jimmy Lu, fix(exec): Default-initialize NestedFixedPoint
members
b2a92df83 by Wechar Yu, fix: Spill writer should respect writeBufferSize
for large batches
3fcdb2b33 by Anshul Baliga, docs(sparksql): Document approx_percentile
968ec27fa by Deepak Majeti, fix(build): Prompt Boost build and fix
wget_opts on MacOS
f9327757a by peterenescu, feat(fuzzer): Add support for floating point edge
cases (#19649)
552ec51e2 by Zhenyuan Zhao, fix(nimble): Keep file read options on
small-file preload
8501e6f4a by Pedro Pedreira, feat(benchmark): Benchmark make_row_from_map
over flat maps
c57fd8592 by Wei He, fix(row): Pad fixed-width UnsafeRow arrays (#19668)
13d9d32e3 by Apurva Kumar, fix(iceberg): Count an UPDATE once in the MERGE
row count
51d70eda8 by fzxu, feat: Add flat map support for map_subset (#18629)
399f9d753 by Shruti Shivakumar, feat(arrow): Import 32-bit decimal arrays
e257582f4 by Pedrum Jalali, feat(spark): Implement native transform_keys
11c27c404 by dependabot[bot], build(deps): Bump follow-redirects from
1.16.0 to 1.16.1 in /website
6eeb70ac7 by shanhao-203, docs(nimble): Add Hybrid FlatMap blog post
(#19532)
447e432c9 by Maria Basmanova, feat(exec)!: Simplify fixed point worker
addressing (#19688)
4a4679f80 by Maria Basmanova, test(exec): Fix data race in fixed point test
cursors
c52ff97b4 by Ke, perf(nimble): Add bulkScan to ConstantEncoding (#19581)
3c9fd367a by Apurva Kumar, fix(iceberg): Report NIMBLE data files as NIMBLE
in Iceberg commit messages
42b5ef84a by Apurva Kumar, fix(iceberg): Keep partition data on MERGE
deletion vectors
0cba44b3c by Matt Karrmann, fix(exec): Avoid division by zero in
AdaptivePrefetch (#19614)
d0f64a95e by Xiaoxuan, feat(nimble): Add synchronous row access and point
seeks to index iterators
959199f65 by Ke, perf(nimble): Bulk-decode hooks and filter-only reads in
FBW, BBP, SubIntSplit (#19572)
0235167b2 by Maria Basmanova, feat(exec)!: Decide fixed point convergence
on worker 0 (#19622)
aa55e9e6e by PRASHANT GOLASH, feat(nimble): Simplify ALP-RD size estimation
and selection (#19497)
7fdce0438 by Maria Basmanova, fix(core)!: Check the plans inside a fixed
point (#19676)
Signed-off-by: glutenperfbot <[email protected]>
* optimize CI uage
Signed-off-by: Yuan <[email protected]>
---------
Signed-off-by: glutenperfbot <[email protected]>
Signed-off-by: Yuan <[email protected]>
Co-authored-by: glutenperfbot <[email protected]>
Co-authored-by: Yuan <[email protected]>
---
.../workflows/util/delta-spark-ut/flaky-tests.txt | 7 +
.github/workflows/util/parallel-ut/Discover.java | 52 +++
.github/workflows/util/parallel-ut/mvn-test.sh | 30 ++
.github/workflows/util/parallel-ut/record-fork.sh | 25 ++
.github/workflows/util/parallel-ut/run.py | 481 +++++++++++++++++++++
.github/workflows/velox_backend_enhanced.yml | 14 +-
.github/workflows/velox_backend_x86.yml | 73 ++--
ep/build-velox/src/get-velox.sh | 4 +-
pom.xml | 4 +
9 files changed, 653 insertions(+), 37 deletions(-)
diff --git a/.github/workflows/util/delta-spark-ut/flaky-tests.txt
b/.github/workflows/util/delta-spark-ut/flaky-tests.txt
index e06a65b2bd..ea2d2d79dc 100644
--- a/.github/workflows/util/delta-spark-ut/flaky-tests.txt
+++ b/.github/workflows/util/delta-spark-ut/flaky-tests.txt
@@ -20,3 +20,10 @@
# whack-a-mole), quarantine it by ERROR SIGNATURE in flaky-error-patterns.txt
# instead.
+# Timing-sensitive: the test gives an AvailableNow-triggered CDC stream 10s to
+# terminate (query.awaitTermination(10000L)). With 4 forked JVMs near the
runner's
+# memory limit, the batch can still be in Gluten's Parquet metadata validation
+# (ParquetMetadataUtils listing files via RawLocalFileSystem, which forks `ls`
per
+# file for permissions), so it times out. Failed once in 25 runs, on the
+# Velox-bump PR #13418 (run 39902045267), which changed no Java code.
+org.apache.spark.sql.delta.DeltaCDCStream*Suite#maxFilesPerTrigger with
Trigger.AvailableNow respects read limits
diff --git a/.github/workflows/util/parallel-ut/Discover.java
b/.github/workflows/util/parallel-ut/Discover.java
new file mode 100644
index 0000000000..82a0661509
--- /dev/null
+++ b/.github/workflows/util/parallel-ut/Discover.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Lists the ScalaTest suites ScalaTest's Runner would discover on the given
runpath, one per line
+ * as {@code <suite>\t<comma-separated class annotations>}. Launched by run.py
in source-file mode
+ * with the recorded fork's classpath, so it works for both Scala 2.12 and
2.13.
+ */
+public class Discover {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public static void main(String[] args) throws Exception {
+ scala.collection.immutable.List runpath =
scala.collection.immutable.Nil$.MODULE$;
+ for (int i = args.length - 1; i >= 0; i--) {
+ runpath = new scala.collection.immutable.$colon$colon(args[i], runpath);
+ }
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ // The same (package-private in Scala, public in bytecode) helper Runner
uses for discovery.
+ scala.collection.Iterator suites =
+ org.scalatest.tools.SuiteDiscoveryHelper.discoverSuiteNames(
+ runpath, loader, scala.Option.empty())
+ .iterator();
+ StringBuilder out = new StringBuilder();
+ while (suites.hasNext()) {
+ String suite = (String) suites.next();
+ StringBuilder annotations = new StringBuilder();
+ for (Annotation annotation : Class.forName(suite, false,
loader).getAnnotations()) {
+ if (annotations.length() > 0) {
+ annotations.append(',');
+ }
+ annotations.append(annotation.annotationType().getName());
+ }
+ out.append(suite).append('\t').append(annotations).append('\n');
+ }
+ System.out.print(out);
+ }
+}
diff --git a/.github/workflows/util/parallel-ut/mvn-test.sh
b/.github/workflows/util/parallel-ut/mvn-test.sh
new file mode 100755
index 0000000000..e16c64670e
--- /dev/null
+++ b/.github/workflows/util/parallel-ut/mvn-test.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Usage: mvn-test.sh <maven test command...>
+#
+# Runs the Maven command with every ScalaTest fork recorded instead of run
(Maven still
+# compiles and runs the JUnit tests), then runs the recorded suites in
${UT_PARALLELISM:-3}
+# concurrent JVMs with run.py. Extra run.py options can be passed in
$PARALLEL_UT_OPTS.
+set -euo pipefail
+here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+GLUTEN_UT_FORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/gluten-ut-forks-XXXXXX")
+export GLUTEN_UT_FORK_DIR
+"$@" -Dscalatest.jvm="$here/record-fork.sh"
+# CentOS 8 images without a python3 module still ship platform-python.
+python=$(command -v python3 || echo /usr/libexec/platform-python)
+# shellcheck disable=SC2086
+"$python" "$here/run.py" --jobs "${UT_PARALLELISM:-3}" ${PARALLEL_UT_OPTS:-}
diff --git a/.github/workflows/util/parallel-ut/record-fork.sh
b/.github/workflows/util/parallel-ut/record-fork.sh
new file mode 100755
index 0000000000..539f8a12b7
--- /dev/null
+++ b/.github/workflows/util/parallel-ut/record-fork.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stand-in for the java executable of scalatest-maven-plugin
(-Dscalatest.jvm=<this file>).
+# Instead of running the suites, it saves the fork's working directory,
environment and
+# arguments under $GLUTEN_UT_FORK_DIR so run.py can replay them across
parallel JVMs.
+set -euo pipefail
+mkdir -p "${GLUTEN_UT_FORK_DIR:?GLUTEN_UT_FORK_DIR must be set}"
+out=$(mktemp -d "$GLUTEN_UT_FORK_DIR/fork-XXXXXX")
+pwd > "$out/cwd"
+env -0 > "$out/env"
+printf '%s\0' "$@" > "$out/args"
diff --git a/.github/workflows/util/parallel-ut/run.py
b/.github/workflows/util/parallel-ut/run.py
new file mode 100644
index 0000000000..5ae55ff634
--- /dev/null
+++ b/.github/workflows/util/parallel-ut/run.py
@@ -0,0 +1,481 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Replays recorded ScalaTest forks across parallel JVMs.
+
+The Spark UT jobs run `mvn test` with -Dscalatest.jvm=record-fork.sh, so every
+module's ScalaTest fork is saved (working directory, environment and arguments,
+which already include the module's classpath, tags, -DwildcardSuites and
+argLine) instead of being run serially inside Maven. This script discovers the
+suites each fork would run, splits them into chunks and runs the chunks in
+--jobs concurrent JVMs. Chunks of one module reuse its recorded command with
the
+suite selection replaced by -s, so the result matches the serial Maven run.
+
+Many suites depend on JVM-wide state left by the suites before them, e.g.
Gluten's
+once-per-JVM static initialization or a leftover default SparkSession. So a
chunk
+is a contiguous slice of the order the serial run uses, and the span of suites
+matching a --sticky-pattern, plus the suite before it, is never split. Suites
+matching a --colocate-pattern write the same fixed paths, so they share a
chunk.
+
+Every JVM keeps the module basedir as working directory, which some suites
+resolve paths against, and gets its own java.io.tmpdir, derby.system.home and
+gluten.test.dir so concurrent suites do not share scratch space. Chunk logs go
+to <module>/target/parallel-ut-<chunk>.log; JUnit XML reports go where Maven
+would have written them.
+"""
+
+import argparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import threading
+import time
+from concurrent.futures import ThreadPoolExecutor
+
+RUNNER = "org.scalatest.tools.Runner"
+DISCOVER = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"Discover.java")
+# Runner options that take a value. Reporter options (-f, -h, -u, -C) may carry
+# glued configuration letters, e.g. -fW <file>.
+VALUE_OPTS = set("-R -l -n -s -w -m -z -t -q -j -b -M -A -F -T -y -i".split())
+REPORTER_OPTS = ("-f", "-h", "-u", "-C")
+FLAG_OPTS = ("-o", "-e", "-P")
+SELECTION_OPTS = {"-s", "-w", "-m"}
+# Selections the replay cannot split; such forks run unchanged in one JVM.
+UNSPLITTABLE_OPTS = {"-z", "-t", "-i", "-j", "-b", "-A"}
+TESTS_LINE = re.compile(
+ r"Tests: succeeded (\d+), failed (\d+), canceled (\d+), "
+ r"ignored (\d+), pending (\d+)"
+)
+FAILURE_LINE = re.compile(r"\*\*\* (FAILED|ABORTED|RUN ABORTED) \*\*\*")
+
+print_lock = threading.Lock()
+
+
+def log(message):
+ with print_lock:
+ print(message, flush=True)
+
+
+class Fork(object):
+ """One recorded scalatest-maven-plugin fork, i.e. one Maven module."""
+
+ def __init__(self, path):
+ with open(os.path.join(path, "cwd")) as f:
+ self.cwd = f.read().strip()
+ with open(os.path.join(path, "env"), "rb") as f:
+ entries = [e.decode() for e in f.read().split(b"\0") if e]
+ self.env = dict(e.split("=", 1) for e in entries if "=" in e)
+ with open(os.path.join(path, "args"), "rb") as f:
+ args = [a.decode() for a in f.read().split(b"\0")[:-1]]
+ split = args.index(RUNNER)
+ self.jvm_args = args[:split]
+ self.options = parse_options(args[split + 1 :])
+ self.module = os.path.relpath(self.cwd, os.getcwd())
+
+ def values(self, name):
+ return [value for opt, value in self.options if opt == name]
+
+ def splittable(self):
+ return not any(opt in UNSPLITTABLE_OPTS for opt, _ in self.options)
+
+ def java(self):
+ java_home = self.env.get("JAVA_HOME")
+ return os.path.join(java_home, "bin", "java") if java_home else "java"
+
+
+def parse_options(args):
+ options = []
+ i = 0
+ while i < len(args):
+ opt = args[i]
+ if opt in VALUE_OPTS or opt.startswith(REPORTER_OPTS):
+ if i + 1 >= len(args):
+ raise ValueError("Missing value for ScalaTest option " + opt)
+ options.append((opt, args[i + 1]))
+ i += 2
+ elif opt.startswith(FLAG_OPTS):
+ options.append((opt, None))
+ i += 1
+ else:
+ raise ValueError("Unsupported ScalaTest option " + opt)
+ return options
+
+
+def runpath(fork):
+ entries = []
+ for value in fork.values("-R"):
+ entries += [
+ e.replace("\\ ", " ") for e in re.split(r"(?<!\\) +",
value.strip()) if e
+ ]
+ return entries
+
+
+def discover(fork):
+ """Returns [(suite, weight)] for the suites the recorded fork would run,
in the
+ order the serial run uses."""
+ paths = runpath(fork)
+ out = subprocess.run(
+ [fork.java(), DISCOVER] + paths,
+ cwd=fork.cwd,
+ env=fork.env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ )
+ if out.returncode != 0:
+ raise RuntimeError(
+ "Suite discovery failed for {}:\n{}".format(fork.module,
out.stderr)
+ )
+ explicit = fork.values("-s")
+ wildcards = fork.values("-w")
+ members = fork.values("-m")
+ include = set(t for v in fork.values("-n") for t in v.split())
+ exclude = set(t for v in fork.values("-l") for t in v.split())
+ discovered = []
+ for line in out.stdout.splitlines():
+ name, _, annotations = line.partition("\t")
+ discovered.append((name, set(annotations.split(",")) if annotations
else set()))
+ # ScalaTest's Runner runs -s suites first, then one DiscoverySuite per -w
and per
+ # -m in argument order, each in discovery order
(DiscoverySuite.wildcardList /
+ # narrowList). A suite matched twice runs once here.
+ if explicit or wildcards or members:
+ ordered = list(explicit)
+ for w in wildcards:
+ ordered += [name for name, _ in discovered if name.startswith(w)]
+ for m in members:
+ ordered += [
+ name
+ for name, _ in discovered
+ if name.startswith(m + ".") and "." not in name[len(m) + 1 :]
+ ]
+ else:
+ ordered = [name for name, _ in discovered]
+ tags_of = dict(discovered)
+ suites, seen = [], set()
+ for name in ordered:
+ if name in seen:
+ continue
+ seen.add(name)
+ weight = class_size(paths, name)
+ # Class-level tags decide most suites up front. Those that run nothing
still
+ # start, so keep a small weight rather than dropping them.
+ tags = tags_of.get(name, set())
+ if (include and not tags & include) or tags & exclude:
+ weight = max(1, weight // 50)
+ suites.append((name, weight))
+ return suites
+
+
+def class_size(paths, suite):
+ """Bytecode size of the suite and its nested classes, a proxy for its
runtime."""
+ relative = suite.replace(".", "/")
+ size = 0
+ for path in paths:
+ directory = os.path.join(path, os.path.dirname(relative))
+ if not os.path.isdir(directory):
+ continue
+ base = os.path.basename(relative)
+ for f in os.listdir(directory):
+ if f == base + ".class" or (
+ f.startswith(base + "$") and f.endswith(".class")
+ ):
+ size += os.path.getsize(os.path.join(directory, f))
+ return max(size, 1)
+
+
+class Chunk(object):
+ def __init__(self, fork, suites):
+ self.fork = fork
+ self.suites = [s for s, _ in suites]
+ self.weight = sum(w for _, w in suites)
+ self.id = None
+ self.returncode = None
+ self.seconds = 0
+ self.log = None
+
+
+def make_chunks(fork, suites, target, sticky_patterns, colocate_patterns):
+ """Cuts the serially ordered suites into contiguous chunks of about
`target`
+ weight.
+
+ For each sticky pattern, the span from the suite before its first match to
its
+ last match stays in one chunk, so those suites see the same predecessors
as in
+ the serial run. Suites matching one colocate pattern share fixed paths but
not
+ state: they move next to the first of them, so they run one after another.
+ """
+ if not fork.splittable():
+ return [Chunk(fork, suites)]
+ for pattern in colocate_patterns:
+ members = [s for s in suites if pattern.search(s[0])]
+ if len(members) > 1:
+ first = suites.index(members[0])
+ rest = [s for s in suites if s not in members]
+ suites = rest[:first] + members + rest[first:]
+ # no_cut[i]: suite i must stay in the same chunk as suite i - 1.
+ no_cut = [False] * len(suites)
+ for pattern in sticky_patterns + colocate_patterns:
+ hits = [i for i, s in enumerate(suites) if pattern.search(s[0])]
+ if hits:
+ for i in range(hits[0], hits[-1] + 1):
+ no_cut[i] = True
+ chunks, current = [], []
+ for i, suite in enumerate(suites):
+ if current and not no_cut[i] and sum(w for _, w in current) >= target:
+ chunks.append(Chunk(fork, current))
+ current = []
+ current.append(suite)
+ if current:
+ chunks.append(Chunk(fork, current))
+ return chunks
+
+
+def command(chunk, work, heap, jvm_opts):
+ fork = chunk.fork
+ jvm_args = list(fork.jvm_args)
+ if heap and not any(a.startswith("-Xmx") for a in jvm_args):
+ jvm_args.append("-Xmx" + heap)
+ jvm_args += jvm_opts.split()
+ jvm_args += [
+ "-Djava.io.tmpdir=" + os.path.join(work, "tmp"),
+ "-Dderby.system.home=" + os.path.join(work, "derby"),
+ "-Dgluten.test.dir=" + os.path.join(work, "gluten"),
+ ]
+ runner_args = []
+ for opt, value in fork.options:
+ if fork.splittable() and opt in SELECTION_OPTS:
+ continue
+ if opt.startswith("-f") and value:
+ # File reporters would overwrite each other; give every chunk its
own file.
+ root, ext = os.path.splitext(value)
+ value = "{}-{}{}".format(root, chunk.id, ext)
+ runner_args.append(opt)
+ if value is not None:
+ runner_args.append(value)
+ if fork.splittable():
+ for suite in chunk.suites:
+ runner_args += ["-s", suite]
+ return [fork.java()] + jvm_args + [RUNNER] + runner_args
+
+
+def run_chunk(chunk, args, total):
+ fork = chunk.fork
+ work = tempfile.mkdtemp(prefix="chunk-{}-".format(chunk.id),
dir=args.work_dir)
+ for sub in ("tmp", "derby", "gluten"):
+ os.makedirs(os.path.join(work, sub))
+ target = os.path.join(fork.cwd, "target")
+ os.makedirs(target, exist_ok=True)
+ chunk.log = os.path.join(target, "parallel-ut-{}.log".format(chunk.id))
+ cmd = command(chunk, work, args.heap, args.jvm_opts)
+ start = time.time()
+ with open(chunk.log, "w") as out:
+ out.write("# " + " ".join(cmd) + "\n")
+ out.flush()
+ try:
+ chunk.returncode = subprocess.run(
+ cmd,
+ cwd=fork.cwd,
+ env=fork.env,
+ stdout=out,
+ stderr=subprocess.STDOUT,
+ timeout=args.timeout or None,
+ ).returncode
+ except subprocess.TimeoutExpired:
+ out.write(
+ "\n*** RUN ABORTED *** chunk timed out after
{}s\n".format(args.timeout)
+ )
+ chunk.returncode = -1
+ chunk.seconds = time.time() - start
+ with open(chunk.log, errors="replace") as f:
+ text = f.read()
+ chunk.passed = (
+ chunk.returncode == 0
+ and "Run completed in" in text
+ and "*** RUN ABORTED ***" not in text
+ )
+ # Keep a failed chunk's scratch directory: some suites write the actual
output
+ # they compared (e.g. plan stability's *.actual.*.txt) to java.io.tmpdir.
+ if chunk.passed:
+ shutil.rmtree(work, ignore_errors=True)
+ else:
+ text += "\nScratch directory kept for inspection: {}\n".format(work)
+ with print_lock:
+ print(
+ "::group::[{}] chunk {}/{} {} ({} suites, {:.0f}s)".format(
+ "PASS" if chunk.passed else "FAIL",
+ chunk.id,
+ total,
+ fork.module,
+ len(chunk.suites),
+ chunk.seconds,
+ )
+ )
+ print(text)
+ print("::endgroup::", flush=True)
+ chunk.counts = [
+ sum(int(m.group(i)) for m in TESTS_LINE.finditer(text)) for i in
range(1, 6)
+ ]
+ chunk.failures = [
+ line.strip() for line in text.splitlines() if FAILURE_LINE.search(line)
+ ]
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter
+ )
+ parser.add_argument(
+ "--forks-dir",
+ default=os.environ.get("GLUTEN_UT_FORK_DIR"),
+ help="Directory record-fork.sh wrote to (default:
$GLUTEN_UT_FORK_DIR)",
+ )
+ parser.add_argument("--jobs", type=int, default=3, help="Concurrent test
JVMs")
+ parser.add_argument(
+ "--chunks-per-job",
+ type=int,
+ default=6,
+ help="Chunks per JVM slot; more balances better, fewer starts fewer
JVMs",
+ )
+ parser.add_argument(
+ "--heap",
+ default="3g",
+ help="-Xmx for each test JVM unless the fork already sets one",
+ )
+ parser.add_argument(
+ "--jvm-opts",
+ # Fewer GC threads per JVM, since the JVMs share few cores. Do not add
+ # -XX:TieredStopAtLevel=1: C1-only code made suites several times
slower and
+ # its larger frames overflowed the stack in Spark's variant tests.
+ default="-XX:ParallelGCThreads=2 -XX:ConcGCThreads=1",
+ help="Extra options for each test JVM",
+ )
+ parser.add_argument(
+ "--sticky-pattern",
+ action="append",
+ help="Regex of suites that must run after the same suites as in the
serial "
+ "run (repeatable). Default: Hive suites, which share the TestHive
session, "
+ "its added jars and a warehouse; and VeloxTransitionSuite, which
initializes "
+ "Gluten from a mock executor context and breaks a later real driver
start "
+ "unless a Gluten session already ran in the JVM",
+ )
+ parser.add_argument(
+ "--colocate-pattern",
+ action="append",
+ help="Regex of suites that write the same fixed paths and so must not
run "
+ "concurrently (repeatable). Default: the Velox TPC-H Iceberg suites,
which "
+ "share test-classes/tpch-data-iceberg-velox",
+ )
+ parser.add_argument(
+ "--timeout", type=int, default=0, help="Per-chunk timeout in seconds"
+ )
+ parser.add_argument(
+ "--work-dir", default=None, help="Parent of per-JVM scratch
directories"
+ )
+ args = parser.parse_args()
+ if not args.forks_dir or not os.path.isdir(args.forks_dir):
+ sys.exit(
+ "No recorded forks in {!r}; was Maven run with
-Dscalatest.jvm=record-fork.sh?".format(
+ args.forks_dir
+ )
+ )
+ args.work_dir = args.work_dir or
tempfile.mkdtemp(prefix="gluten-parallel-ut-")
+ os.makedirs(args.work_dir, exist_ok=True)
+
+ forks = sorted(
+ (Fork(os.path.join(args.forks_dir, d)) for d in
os.listdir(args.forks_dir)),
+ key=lambda fork: fork.cwd,
+ )
+ start = time.time()
+ with ThreadPoolExecutor(max_workers=args.jobs) as pool:
+ discovered = list(pool.map(discover, forks))
+ total_weight = sum(w for suites in discovered for _, w in suites)
+ target = max(1, total_weight // max(1, args.jobs * args.chunks_per_job))
+ sticky_patterns = [
+ re.compile(p)
+ for p in args.sticky_pattern
+ or [r"^org\.apache\.spark\.sql\.hive\.", r"\.VeloxTransitionSuite$"]
+ ]
+ colocate_patterns = [
+ re.compile(p)
+ for p in args.colocate_pattern
+ or [r"\.Velox(PartitionedTable)?TPCHIcebergSuite$"]
+ ]
+ chunks = []
+ for fork, suites in zip(forks, discovered):
+ log("{}: {} suites".format(fork.module, len(suites)))
+ if suites:
+ chunks += make_chunks(
+ fork, suites, target, sticky_patterns, colocate_patterns
+ )
+ # Heaviest first, so the long chunks do not end up as the tail.
+ chunks.sort(key=lambda chunk: -chunk.weight)
+ for i, chunk in enumerate(chunks, 1):
+ chunk.id = i
+ log(
+ "Discovered {} suites in {} modules in {:.0f}s; running {} chunks in
{} JVMs".format(
+ sum(len(c.suites) for c in chunks),
+ len(forks),
+ time.time() - start,
+ len(chunks),
+ args.jobs,
+ )
+ )
+ with ThreadPoolExecutor(max_workers=args.jobs) as pool:
+ for future in [
+ pool.submit(run_chunk, chunk, args, len(chunks)) for chunk in
chunks
+ ]:
+ future.result()
+
+ failed = [c for c in chunks if not c.passed]
+ counts = [sum(c.counts[i] for c in chunks) for i in range(5)]
+ print("\nParallel ScalaTest summary ({:.0f}s):".format(time.time() -
start))
+ for chunk in sorted(chunks, key=lambda c: c.id):
+ print(
+ " [{}] chunk {:3d} {:6.0f}s {:4d} suites {} {}".format(
+ "PASS" if chunk.passed else "FAIL",
+ chunk.id,
+ chunk.seconds,
+ len(chunk.suites),
+ chunk.fork.module,
+ os.path.relpath(chunk.log),
+ )
+ )
+ print(
+ "Tests: succeeded {}, failed {}, canceled {}, ignored {}, pending
{}".format(
+ *counts
+ )
+ )
+ if failed:
+ print("\n{} of {} chunks failed:".format(len(failed), len(chunks)))
+ for chunk in failed:
+ print(
+ " chunk {} ({}, exit code {}), log: {}".format(
+ chunk.id,
+ chunk.fork.module,
+ chunk.returncode,
+ os.path.relpath(chunk.log),
+ )
+ )
+ for line in chunk.failures or ["(no failed test reported; see the
log)"]:
+ print(" " + line)
+ sys.exit(1)
+ print("All chunks passed.")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/.github/workflows/velox_backend_enhanced.yml
b/.github/workflows/velox_backend_enhanced.yml
index a38e890066..6520430bf8 100644
--- a/.github/workflows/velox_backend_enhanced.yml
+++ b/.github/workflows/velox_backend_enhanced.yml
@@ -19,6 +19,7 @@ on:
pull_request:
paths:
- '.github/workflows/velox_backend_enhanced.yml'
+ - '.github/workflows/util/parallel-ut/**'
- 'pom.xml'
- 'backends-velox/**'
- 'gluten-uniffle/**'
@@ -45,6 +46,13 @@ env:
CCACHE_DIR: "${{ github.workspace }}/.ccache"
# spark.sql.ansi.enabled defaults to false.
SPARK_ANSI_SQL_MODE: false
+ # The spark-test-* jobs prefix their `mvn test` with this wrapper: Maven
compiles and runs
+ # the JUnit tests, then the ScalaTest suites run in UT_PARALLELISM
concurrent JVMs instead
+ # of one after another inside Maven. See
.github/workflows/util/parallel-ut/run.py.
+ PARALLEL_UT: '.github/workflows/util/parallel-ut/mvn-test.sh'
+ # The runners have 4 vCPUs and a single test JVM already keeps several busy:
with 4
+ # JVMs each suite ran ~5x slower than serially, so total throughput dropped.
+ UT_PARALLELISM: 3
concurrency:
group: ${{ github.repository }}-${{ github.head_ref || github.sha }}-${{
github.workflow }}
@@ -160,7 +168,7 @@ jobs:
java -version
export SPARK_HOME=/opt/shims/spark35/spark_home/
ls -l $SPARK_HOME
- $MVN_CMD clean test -Pspark-3.5 -Pjava-17 -Pbackends-velox -Piceberg
-Pdelta -Phudi \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi \
-DtagsToExclude=org.apache.spark.tags.ExtendedSQLTest,org.apache.spark.tags.SlowHiveTest,org.apache.gluten.tags.UDFTest,org.apache.gluten.tags.CudfTest,org.apache.gluten.tags.SkipTest
\
-DargLine="-Dspark.test.home=$SPARK_HOME"
- name: Upload test report
@@ -207,10 +215,10 @@ jobs:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export PATH=$JAVA_HOME/bin:$PATH
java -version
- $MVN_CMD clean test -Pspark-3.5 -Pjava-17 -Pbackends-velox -Piceberg
-Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.ExtendedSQLTest
- $MVN_CMD clean test -Pspark-3.5 -Pjava-17 -Pbackends-velox -Piceberg
-Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.SlowHiveTest
- name: Upload test report
diff --git a/.github/workflows/velox_backend_x86.yml
b/.github/workflows/velox_backend_x86.yml
index 48dc7d7a71..40ca515c0c 100644
--- a/.github/workflows/velox_backend_x86.yml
+++ b/.github/workflows/velox_backend_x86.yml
@@ -19,6 +19,7 @@ on:
pull_request:
paths:
- '.github/workflows/velox_backend_x86.yml'
+ - '.github/workflows/util/parallel-ut/**'
- 'pom.xml'
- 'backends-velox/**'
- 'gluten-uniffle/**'
@@ -57,6 +58,13 @@ env:
CCACHE_DIR: "${{ github.workspace }}/.ccache"
# spark.sql.ansi.enabled defaults to false.
SPARK_ANSI_SQL_MODE: false
+ # The spark-test-* jobs prefix their `mvn test` with this wrapper: Maven
compiles and runs
+ # the JUnit tests, then the ScalaTest suites run in UT_PARALLELISM
concurrent JVMs instead
+ # of one after another inside Maven. See
.github/workflows/util/parallel-ut/run.py.
+ PARALLEL_UT: '.github/workflows/util/parallel-ut/mvn-test.sh'
+ # The runners have 4 vCPUs and a single test JVM already keeps several busy:
with 4
+ # JVMs each suite ran ~5x slower than serially, so total throughput dropped.
+ UT_PARALLELISM: 3
concurrency:
group: ${{ github.repository }}-${{ github.head_ref || github.sha }}-${{
github.workflow }}
@@ -769,15 +777,15 @@ jobs:
export SPARK_HOME=/opt/shims/spark34/spark_home/
TAGS_EXCLUDE="org.apache.spark.tags.ExtendedSQLTest,org.apache.spark.tags.SlowHiveTest,org.apache.gluten.tags.UDFTest,org.apache.gluten.tags.EnhancedFeaturesTest,org.apache.gluten.tags.CudfTest,org.apache.gluten.tags.SkipTest"
if [ "${{ matrix.group }}" = "1" ]; then
- $MVN_CMD clean test -Pspark-3.4 -Pjava-17 -Pbackends-velox
-Piceberg -Piceberg-test -Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.4 -Pjava-17
-Pbackends-velox -Piceberg -Piceberg-test -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DtagsToExclude="$TAGS_EXCLUDE"
-DargLine="-Dspark.test.home=$SPARK_HOME" \
-DwildcardSuites="org.apache.spark.sql.streaming,org.apache.spark.GlutenSortShuffleSuite,org.apache.gluten"
elif [ "${{ matrix.group }}" = "2" ]; then
- $MVN_CMD clean test -Pspark-3.4 -Pjava-17 -Pbackends-velox
-Piceberg -Piceberg-test -Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.4 -Pjava-17
-Pbackends-velox -Piceberg -Piceberg-test -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DtagsToExclude="$TAGS_EXCLUDE"
-DargLine="-Dspark.test.home=$SPARK_HOME" \
-DwildcardSuites="org.apache.spark.sql.execution,org.apache.spark.sql.catalyst,org.apache.spark.sql.errors,org.apache.spark.sql.extension"
else
- $MVN_CMD clean test -Pspark-3.4 -Pjava-17 -Pbackends-velox
-Piceberg -Piceberg-test -Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.4 -Pjava-17
-Pbackends-velox -Piceberg -Piceberg-test -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DtagsToExclude="$TAGS_EXCLUDE"
-DargLine="-Dspark.test.home=$SPARK_HOME" \
-DwildcardSuites="org.apache.spark.sql.GlutenSQL,org.apache.spark.sql.Gluten,org.apache.spark.sql.connector,org.apache.spark.sql.sources,org.apache.spark.sql.hive,org.apache.spark.sql.gluten,org.apache.spark.sql.shim"
fi
@@ -831,11 +839,11 @@ jobs:
java -version
export SPARK_HOME=/opt/shims/spark34/spark_home/
if [ "${{ matrix.suite }}" = "extended" ]; then
- $MVN_CMD clean test -Pspark-3.4 -Pjava-17 -Pbackends-velox
-Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.4 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DtagsToInclude=org.apache.spark.tags.ExtendedSQLTest \
-DargLine="-Dspark.test.home=$SPARK_HOME"
else
- $MVN_CMD clean test -Pspark-3.4 -Pjava-17 -Pbackends-velox
-Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.4 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DtagsToInclude=org.apache.spark.tags.SlowHiveTest \
-DargLine="-Dspark.test.home=$SPARK_HOME"
fi
@@ -893,15 +901,15 @@ jobs:
java -version
TAGS_EXCLUDE="org.apache.spark.tags.ExtendedSQLTest,org.apache.spark.tags.SlowHiveTest,org.apache.gluten.tags.UDFTest,org.apache.gluten.tags.EnhancedFeaturesTest,org.apache.gluten.tags.CudfTest,org.apache.gluten.tags.SkipTest"
if [ "${{ matrix.group }}" = "1" ]; then
- $MVN_CMD clean test -Pspark-3.5 -Pjava-17 -Pbackends-velox
-Piceberg -Pdelta -Phudi -Ppaimon -Plance -Plance-test -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi -Ppaimon -Plance -Plance-test
-Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.streaming,org.apache.spark.GlutenSortShuffleSuite,org.apache.gluten"
elif [ "${{ matrix.group }}" = "2" ]; then
- $MVN_CMD clean test -Pspark-3.5 -Pjava-17 -Pbackends-velox
-Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.execution,org.apache.spark.sql.catalyst,org.apache.spark.sql.errors,org.apache.spark.sql.extension"
else
- $MVN_CMD clean test -Pspark-3.5 -Pjava-17 -Pbackends-velox
-Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.GlutenSQL,org.apache.spark.sql.Gluten,org.apache.spark.sql.connector,org.apache.spark.sql.sources,org.apache.spark.sql.hive,org.apache.spark.sql.gluten,org.apache.spark.sql.shim"
fi
@@ -929,10 +937,11 @@ jobs:
spark-test-spark35-scala213:
needs: [detect-changes, build-native-lib-centos-8]
- if: >-
- needs.detect-changes.outputs.java == 'true' ||
- needs.detect-changes.outputs.shims35 == 'true' ||
- needs.detect-changes.outputs.cpp == 'true'
+ # Nightly only: the spark40/41/42 jobs already build and test with Scala
2.13 on
+ # every PR, so per-PR this Spark 3.5 + Scala 2.13 combo adds ~90
runner-minutes for
+ # little PR-specific signal. The nightly full_run (or a workflow_dispatch
run)
+ # still covers it daily.
+ if: needs.detect-changes.outputs.full_run == 'true'
runs-on: ubuntu-22.04
strategy:
fail-fast: false
@@ -965,15 +974,15 @@ jobs:
java -version
TAGS_EXCLUDE="org.apache.spark.tags.ExtendedSQLTest,org.apache.spark.tags.SlowHiveTest,org.apache.gluten.tags.UDFTest,org.apache.gluten.tags.EnhancedFeaturesTest,org.apache.gluten.tags.CudfTest,org.apache.gluten.tags.SkipTest"
if [ "${{ matrix.group }}" = "1" ]; then
- $MVN_CMD clean test -Pspark-3.5 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Piceberg -Pdelta -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35-scala-2.13/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.streaming,org.apache.spark.GlutenSortShuffleSuite,org.apache.gluten"
elif [ "${{ matrix.group }}" = "2" ]; then
- $MVN_CMD clean test -Pspark-3.5 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Piceberg -Pdelta -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35-scala-2.13/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.execution,org.apache.spark.sql.catalyst,org.apache.spark.sql.errors,org.apache.spark.sql.extension"
else
- $MVN_CMD clean test -Pspark-3.5 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Piceberg -Pdelta -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35-scala-2.13/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.GlutenSQL,org.apache.spark.sql.Gluten,org.apache.spark.sql.connector,org.apache.spark.sql.sources,org.apache.spark.sql.hive,org.apache.spark.sql.gluten,org.apache.spark.sql.shim"
fi
@@ -1022,11 +1031,11 @@ jobs:
export PATH=$JAVA_HOME/bin:$PATH
java -version
if [ "${{ matrix.suite }}" = "extended" ]; then
- $MVN_CMD clean test -Pspark-3.5 -Pjava-17 -Pbackends-velox
-Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.ExtendedSQLTest
else
- $MVN_CMD clean test -Pspark-3.5 -Pjava-17 -Pbackends-velox
-Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-3.5 -Pjava-17
-Pbackends-velox -Piceberg -Pdelta -Phudi -Ppaimon -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark35/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.SlowHiveTest
fi
@@ -1299,15 +1308,15 @@ jobs:
java -version
TAGS_EXCLUDE="org.apache.spark.tags.ExtendedSQLTest,org.apache.spark.tags.SlowHiveTest,org.apache.gluten.tags.UDFTest,org.apache.gluten.tags.EnhancedFeaturesTest,org.apache.gluten.tags.CudfTest,org.apache.gluten.tags.SkipTest"
if [ "${{ matrix.group }}" = "1" ]; then
- $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pdelta -Ppaimon -Plance -Plance-test -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pdelta -Ppaimon -Plance -Plance-test -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark40/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.streaming,org.apache.spark.GlutenSortShuffleSuite,org.apache.gluten"
elif [ "${{ matrix.group }}" = "2" ]; then
- $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pdelta -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pdelta -Ppaimon -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark40/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.execution,org.apache.spark.sql.catalyst,org.apache.spark.sql.errors,org.apache.spark.sql.extension"
else
- $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pdelta -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pdelta -Ppaimon -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark40/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.GlutenSQL,org.apache.spark.sql.Gluten,org.apache.spark.sql.connector,org.apache.spark.sql.sources,org.apache.spark.sql.hive,org.apache.spark.sql.gluten,org.apache.spark.sql.shim"
fi
@@ -1356,11 +1365,11 @@ jobs:
export PATH=$JAVA_HOME/bin:$PATH
java -version
if [ "${{ matrix.suite }}" = "extended" ]; then
- $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pdelta -Ppaimon -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pdelta -Ppaimon -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark40/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.ExtendedSQLTest
else
- $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pdelta -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.0 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pdelta -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark40/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.SlowHiveTest
fi
@@ -1425,17 +1434,17 @@ jobs:
TAGS_EXCLUDE="org.apache.spark.tags.ExtendedSQLTest,org.apache.spark.tags.SlowHiveTest,org.apache.gluten.tags.UDFTest,org.apache.gluten.tags.EnhancedFeaturesTest,org.apache.gluten.tags.CudfTest,org.apache.gluten.tags.SkipTest"
if [ "${{ matrix.group }}" = "1" ]; then
# Group 1: streaming + gluten utils + top-level spark tests (~55
classes)
- $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pspark-ut -Pdelta \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pspark-ut -Pdelta \
-DargLine="-Dspark.test.home=/opt/shims/spark41/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.streaming,org.apache.spark.GlutenSortShuffleSuite,org.apache.gluten"
elif [ "${{ matrix.group }}" = "2" ]; then
# Group 2: execution + catalyst + errors + extension tests (~140
classes)
- $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pspark-ut -Pdelta \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pspark-ut -Pdelta \
-DargLine="-Dspark.test.home=/opt/shims/spark41/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.execution,org.apache.spark.sql.catalyst,org.apache.spark.sql.errors,org.apache.spark.sql.extension"
else
# Group 3: top-level sql, connector, sources, hive and remaining
tests (~205 classes)
- $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pspark-ut -Pdelta \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pspark-ut -Pdelta \
-DargLine="-Dspark.test.home=/opt/shims/spark41/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.GlutenSQL,org.apache.spark.sql.Gluten,org.apache.spark.sql.connector,org.apache.spark.sql.sources,org.apache.spark.sql.hive,org.apache.spark.sql.gluten,org.apache.spark.sql.shim"
fi
@@ -1485,11 +1494,11 @@ jobs:
export PATH=$JAVA_HOME/bin:$PATH
java -version
if [ "${{ matrix.suite }}" = "extended" ]; then
- $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pdelta -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pdelta -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark41/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.ExtendedSQLTest
else
- $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.1 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark41/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.SlowHiveTest
fi
@@ -1557,17 +1566,17 @@ jobs:
$MVN_CMD clean install -Pspark-4.2 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pspark-ut -Pdelta -DskipTests
if [ "${{ matrix.group }}" = "1" ]; then
# Group 1: streaming + gluten utils + top-level spark tests (~55
classes)
- $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pspark-ut -Pdelta \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pspark-ut -Pdelta \
-DargLine="-Dspark.test.home=/opt/shims/spark42/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.streaming,org.apache.spark.GlutenSortShuffleSuite,org.apache.gluten"
elif [ "${{ matrix.group }}" = "2" ]; then
# Group 2: execution + catalyst + errors + extension tests (~140
classes)
- $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pspark-ut -Pdelta \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pspark-ut -Pdelta \
-DargLine="-Dspark.test.home=/opt/shims/spark42/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.execution,org.apache.spark.sql.catalyst,org.apache.spark.sql.errors,org.apache.spark.sql.extension"
else
# Group 3: top-level sql, connector, sources, hive and remaining
tests (~205 classes)
- $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pspark-ut -Pdelta \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pspark-ut -Pdelta \
-DargLine="-Dspark.test.home=/opt/shims/spark42/spark_home/"
-DtagsToExclude="$TAGS_EXCLUDE" \
-DwildcardSuites="org.apache.spark.sql.GlutenSQL,org.apache.spark.sql.Gluten,org.apache.spark.sql.connector,org.apache.spark.sql.sources,org.apache.spark.sql.hive,org.apache.spark.sql.gluten,org.apache.spark.sql.shim"
fi
@@ -1620,11 +1629,11 @@ jobs:
# sibling Gluten modules (gluten-substrait etc.) before testing.
$MVN_CMD clean install -Pspark-4.2 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pdelta -Pspark-ut -DskipTests
if [ "${{ matrix.suite }}" = "extended" ]; then
- $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pdelta -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pdelta -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark42/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.ExtendedSQLTest
else
- $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13 -Pjava-17
-Pbackends-velox -Pspark-ut \
+ $PARALLEL_UT $MVN_CMD clean test -Pspark-4.2 -Pscala-2.13
-Pjava-17 -Pbackends-velox -Pspark-ut \
-DargLine="-Dspark.test.home=/opt/shims/spark42/spark_home/" \
-DtagsToInclude=org.apache.spark.tags.SlowHiveTest
fi
diff --git a/ep/build-velox/src/get-velox.sh b/ep/build-velox/src/get-velox.sh
index 37529d31ec..1fbc9efa9e 100755
--- a/ep/build-velox/src/get-velox.sh
+++ b/ep/build-velox/src/get-velox.sh
@@ -18,8 +18,8 @@ set -exu
CURRENT_DIR=$(cd "$(dirname "$BASH_SOURCE")"; pwd)
VELOX_REPO=https://github.com/IBM/velox.git
-VELOX_BRANCH=dft-2026_10_21
-VELOX_ENHANCED_BRANCH=ibm-2026_10_21
+VELOX_BRANCH=dft-2026_10_23
+VELOX_ENHANCED_BRANCH=ibm-2026_10_23
VELOX_HOME=""
RUN_SETUP_SCRIPT=ON
ENABLE_ENHANCED_FEATURES=OFF
diff --git a/pom.xml b/pom.xml
index ab555f8c2d..4d21411157 100644
--- a/pom.xml
+++ b/pom.xml
@@ -201,6 +201,9 @@
-Dfile.encoding=UTF-8
</extraJavaTestArgs>
<log4j.conf>file:src/test/resources/log4j2.properties</log4j.conf>
+ <!-- Java executable for ScalaTest forks; empty means the JDK running
Maven. CI points
+ it at .github/workflows/util/parallel-ut/record-fork.sh to run suites
in parallel. -->
+ <scalatest.jvm/>
</properties>
<dependencyManagement>
@@ -623,6 +626,7 @@
<testFailureIgnore>${maven.test.failure.ignore}</testFailureIgnore>
<junitxml>.</junitxml>
<argLine>${argLine} ${extraJavaTestArgs}</argLine>
+ <jvm>${scalatest.jvm}</jvm>
<systemProperties>
<log4j.configurationFile>${log4j.conf}</log4j.configurationFile>
</systemProperties>
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]