Skip to content

Add spark-gluten-clickhouse entry (Spark + Gluten with the CH backend)#861

Open
alexey-milovidov wants to merge 8 commits into
mainfrom
add-spark-gluten-clickhouse
Open

Add spark-gluten-clickhouse entry (Spark + Gluten with the CH backend)#861
alexey-milovidov wants to merge 8 commits into
mainfrom
add-spark-gluten-clickhouse

Conversation

@alexey-milovidov

Copy link
Copy Markdown
Member

Summary

  • Adds a spark-gluten-clickhouse/ entry that runs Apache Spark with Apache Gluten configured to use the ClickHouse backend (spark.gluten.sql.columnar.backend.lib=ch). Gluten loads libch.so (a fork of ClickHouse v23.1) into the Spark executor JVM and runs the columnar physical plan natively through it.
  • Complements spark-gluten/ (Velox backend) and the proposed spark-velox/ (Add spark-velox entry (Spark + Velox via Apache Gluten) #858) — this entry exercises a meaningfully different execution path: Catalyst → Substrait → ClickHouse engine, rather than Catalyst → Substrait → Velox.

Build

No pre-built bundle is published for the CH backend (Apache Gluten v1.4.0 ships only the Velox bundle, and Maven Central has nothing). benchmark.sh therefore builds two things from source:

  1. libch.so — built from Kyligence/ClickHouse at the branch pinned in gluten/cpp-ch/clickhouse.version (currently rebase_ch/20250326). Uses Clang 18 / cmake / ninja.
  2. The Gluten Spark plugin — built via Maven with -Pbackends-clickhouse,spark-3.5,scala-2.12 under JDK 8.

Limitations

  • The libch.so compile is essentially a ClickHouse build and is RAM-hungry; Gluten's docs recommend ≥64 GB. On c6a.4xlarge (32 GB) it may OOM — c6a.8xlarge or larger is recommended for a clean run, hence the default machine label in benchmark.sh.
  • ARM is untested. Both ClickHouse and the Gluten plugin should compile on aarch64 in principle, but the Gluten CI does not publish CH-backend artifacts for ARM.

Notes

  • Queries use ClickHouse-style regex backreferences (\1) rather than Spark's $1, because regex evaluation runs inside libch.so. This was anticipated in the existing spark-gluten/README.md and Gluten issue #7545.
  • Memory split between Spark heap and the Gluten off-heap pool is 50/50, identical to the Velox entry — the CH backend also runs off-heap via JNI.

Test plan

  • Run on an x86_64 c6a.8xlarge.
  • Verify benchmark.sh clones gluten + Kyligence/ClickHouse, builds libch.so and the Spark plugin, runs all 43 queries, and writes results/<machine>.json.

🤖 Generated with Claude Code

alexey-milovidov and others added 4 commits May 7, 2026 12:23
Adds a spark-gluten-clickhouse/ entry that runs the ClickBench query
suite against Apache Spark with Apache Gluten configured to use the
ClickHouse backend ('ch'), in which Gluten loads libch.so (a fork of
ClickHouse v23.1) into the Spark executor JVM and runs the columnar
plan natively through it.

Compared with spark-gluten/ (which uses the Velox backend), this
exercises a meaningfully different execution path: Catalyst -> Substrait
-> ClickHouse engine, rather than Catalyst -> Substrait -> Velox.

No pre-built bundle is published for the CH backend (the Apache Gluten
release tarball ships only the Velox bundle), so benchmark.sh builds
both libch.so and the Gluten Spark plugin from source. The build is
memory-hungry; a 64 GB host (c6a.8xlarge or larger) is recommended.

Queries use ClickHouse-style regex backreferences (\1) since the regex
evaluation runs inside libch.so, as anticipated in the spark-gluten/
README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The pinned Kyligence/ClickHouse fork now rejects Clang < 19 in
cmake/tools.cmake, so installing Clang 18 fails the configure step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

The run of spark-gluten-clickhouse on c6a.4xlarge did not produce results.

Logs:

The entry was written in the old self-contained benchmark.sh style, which
predates lib/benchmark-common.sh. The c6a.4xlarge run (PR #861) failed
immediately at `../download-hits-parquet-single: No such file or directory`
— the download helpers moved into lib/ and are now invoked by the harness,
not by each entry.

Refactor to match the sibling spark-gluten/ (Velox) and spark-velox/ entries:

- benchmark.sh: thin wrapper that sets BENCH_* env and exec's
  ../lib/benchmark-common.sh (RESTARTABLE=no, concurrent QPS skipped per #946).
- install: the from-source build (libch.so + Gluten CH plugin), moved out of
  benchmark.sh. Fixes verified against Gluten v1.4.0's own build_clickhouse.sh:
    * clone apache/incubator-gluten (canonical repo, matches docs) at v1.4.0;
    * read CH_ORG as well as CH_BRANCH from cpp-ch/clickhouse.version;
    * the build lands in cpp-ch/build_ch/ (not build/ as the old path assumed
      and the prose docs still say) — glob for libch.so instead of hardcoding;
    * set MAVEN_OPTS per the Gluten docs so the Maven build doesn't OOM;
    * idempotent (skips clone/build when artifacts already present).
- load/query/start/stop/check/data-size: standard harness scripts. query runs
  Spark under JDK 17 (Gluten is built under JDK 8).
- query.py: single run per invocation, timing as the last stderr line, exit 1
  on failure — the harness contract, replacing the old 3-try/"Time:" loop.
- Drop run.sh (the harness drives the query loop).
- .gitignore: __pycache__/ and the from-source build artifacts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alexey-milovidov alexey-milovidov added the machine:c6a.metal PR benchmark machine override: c6a.metal (192 vCPU, 384 GB, AMD) label Jul 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

The run of spark-gluten-clickhouse on c6a.metal did not produce results.

Logs:

…JNI/AWT

The c6a.metal re-run got past the download fix and into the ClickHouse
compile, then failed at the extern-local-engine cmake configure:

    -- Could NOT find JNI (missing: AWT)
    CMake Error: ... JAVA_AWT_INCLUDE_PATH (ADVANCED) ... set to NOTFOUND

ClickHouse's extern-local-engine links against JNI including AWT, but the
install used openjdk-17-jdk-headless, which ships neither jawt.h nor
libjawt.so (verified against the Ubuntu noble package file lists). Switch to
the full openjdk-17-jdk (ships jawt.h; depends on the non-headless JRE that
ships libjawt.so) and export JAVA_HOME to it so cmake's FindJNI probes the
right, AWT-complete JDK deterministically. JDK 8 stays headless — it is only
the Maven build, which doesn't need AWT.

Also corrects the README: the outer wrapper cmakes into cpp-ch/build_ch, but
the inner ClickHouse cmake builds libch.so under cpp-ch/build/ (install globs
for it under cpp-ch/ either way).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

The run of spark-gluten-clickhouse on c6a.metal did not produce results.

Logs:

The c6a.metal run got all the way through the libch.so compile (all 12976
ninja steps linked — the JDK/AWT fix worked) and built every Gluten Maven
module except `backends-clickhouse`, which failed its `enforce-delta-profile`
enforcer rule:

    "-P delta" must be set when building Gluten with ClickHouse backend.
    Profile "delta" is not activated.

Add `-Pdelta` to the `mvn` invocation. The `spark-3.5` profile already pins
the matching `delta.version` (3.2.0, package `delta-spark`), so no extra
version flag is needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

The run of spark-gluten-clickhouse on c6a.metal did not produce results.

Logs:

Run 4 (with -Pdelta) got all the way through the libch.so compile, the Maven
backends-clickhouse build, and data loading (~14.8 GB), then failed at query
time when the driver JVM lazily loaded the native engine:

    java.lang.UnsatisfiedLinkError: .../libch.so:
    cannot allocate memory in static TLS block
        at org.apache.gluten.backendsapi.clickhouse.CHListenerApi.initialize
        ... CHBackend.onDriverStart ... GlutenDriverPlugin.init

libch.so carries initial-exec-model TLS (from its statically linked deps), and
the static TLS block is sized at process startup, so a lazy `dlopen` from the
already-running JVM has no room left. Gluten's own docs work around this with
`spark.executorEnv.LD_PRELOAD=<libch.so>`, but in local[*] mode the driver JVM
*is* the executor and is launched before any Spark config is read, so
executorEnv never applies.

Preload it via the JVM's environment instead: set `LD_PRELOAD` in query.py
before `getOrCreate()`. This does not affect the already-started Python
process, but pyspark copies os.environ into the JVM it spawns, so the JVM
preloads libch.so at startup while the static TLS block still has room;
Gluten's later `System.load()` then reuses the already-loaded library. Also set
`spark.executorEnv.LD_PRELOAD` for correctness under a future cluster-mode run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alexey-milovidov alexey-milovidov added machine:c6a.metal PR benchmark machine override: c6a.metal (192 vCPU, 384 GB, AMD) and removed machine:c6a.metal PR benchmark machine override: c6a.metal (192 vCPU, 384 GB, AMD) labels Jul 23, 2026
@alexey-milovidov
alexey-milovidov deployed to benchmark-approval July 23, 2026 07:37 — with GitHub Actions Active
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

machine:c6a.metal PR benchmark machine override: c6a.metal (192 vCPU, 384 GB, AMD)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant