Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions .github/workflows/hudi_trino_e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: Hudi Trino E2E

on:
push:
branches:
- master
- 'release-*'
# TEMPORARY: validate the workflow on the dev branch; drop this line
# (and the commit that added it) before merging upstream.
- trino-e2e-testcontainers

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the squash merge button is enabled in .asf.yaml, so there is no separate commit to drop at merge time and this line ships to master unless it comes off the branch head first. Please strip it before this goes in.

paths:
- 'hudi-trino/**'
- 'docker/trino/**'
- 'docker/compose/docker-compose_hadoop340_hive2310_spark402*'
- 'docker/demo/sparksql-stock-ticks-trino.commands'
- 'hudi-integ-test/src/test/java/org/apache/hudi/integ2/**'
- '.github/workflows/hudi_trino_e2e.yml'
pull_request:
branches:
- master
- 'release-*'
paths:
- 'hudi-trino/**'
- 'docker/trino/**'
- 'docker/compose/docker-compose_hadoop340_hive2310_spark402*'
- 'docker/demo/sparksql-stock-ticks-trino.commands'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ITTestTrinoCustomType also drives sparksql-blob-type-sql, sparksql-blob-type-df, sparksql-vector-type-sql, sparksql-variant-type-sql and setup_demo_container.sh, so editing any of those cannot trigger this workflow and the break only surfaces on the next unrelated hudi-trino PR. Widen this entry to docker/demo/**.

- 'hudi-integ-test/src/test/java/org/apache/hudi/integ2/**'
- '.github/workflows/hudi_trino_e2e.yml'
workflow_dispatch:

concurrency:
group: hudi-trino-e2e-${{ github.ref }}
cancel-in-progress: ${{ !contains(github.ref, 'master') && !contains(github.ref, 'release-') }}

env:
MVN_ARGS: -e -ntp -B -V -Dgpg.skip -Djacoco.skip -Pwarn-log

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This drops the aether.connector.http.retryHandler.* flags that bot.yml carries, and the JDK 17 step here is exactly the cold-cache full-reactor build those were added for. Copy the retry block over from bot.yml's MVN_ARGS.

SCALA_PROFILE: -Dscala-2.13 -Dscala.binary.version=2.13
COMPOSE_PREFIX: docker-compose_hadoop340_hive2310_spark402

jobs:
trino-e2e:
# Testcontainers E2E for the RFC-105 native trino-hudi connector: builds
# hudi-trino at HEAD, assembles the plugin dir via the in-repo shim
# (docker/trino/shim, standing in for the not-yet-released upstream
# trinodb/trino plugin/trino-hudi shim), bakes it into a local
# apachehudi/hudi-trino_481 image, and runs ITTestTrino* against the
# spark402 compose stack (the only pair with the trinocoordinator service).
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/share/boost
docker system prune --all --force --volumes
- name: Pre-pull compose images (fails fast if not published)
run: |
# Surface a missing sparkadhoc image before the long Maven install. The
# remaining stack images are pulled by docker-compose at test time; the
# trino image is built locally below, never pulled.
docker pull apachehudi/hudi-hadoop_3.4.0-hive_2.3.10-sparkadhoc_4.0.2:latest
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
architecture: x64
cache: maven
- name: Build and install Hudi artifacts (JDK 17)
# Full reactor: the compose containers mount the workspace and the tests
# use bundles staged by the -Pintegration-tests build (e.g.
# docker/hoodie/hadoop/hive_base/target/hoodie-spark-bundle.jar).
run:
mvn clean install -T 2 $SCALA_PROFILE -Dspark4.0 -Dflink1.20 -Pintegration-tests -DskipTests=true -Ddocker.compose.skip=true $MVN_ARGS
- name: Set up JDK 25
uses: actions/setup-java@v5
with:
java-version: '25'
distribution: 'temurin'
cache: maven
- name: Build hudi-trino connector (JDK 25)
# No trinodb/trino checkout needed: the unpublished Trino test-jars sit
# behind the off-by-default hudi-trino-tests profile and packaging
# resolves entirely from Maven Central.
run:
mvn $MVN_ARGS -Phudi-trino -pl hudi-trino install -Dmaven.test.skip=true
- name: Assemble trino-hudi plugin dir via in-repo shim (JDK 25)
# package, NOT install: installing would shadow the real
# io.trino:trino-hudi release coordinates in the local m2.
run:
mvn $MVN_ARGS -f docker/trino/shim/pom.xml clean package -DskipTests
- name: Build apachehudi/hudi-trino_481 image
run: |
docker/trino/build_image.sh --plugin-dir docker/trino/shim/target/trino-hudi-481
# Sanity: the shim must have produced a populated plugin dir with a
# service descriptor jar, or Trino cannot load the plugin at boot.
echo "plugin dir jar count: $(ls docker/trino/shim/target/trino-hudi-481 | wc -l)"
ls docker/trino/shim/target/trino-hudi-481/*services*.jar
- name: Smoke-boot the Trino image standalone
# Catches image-level boot failures (plugin load errors, bad etc/ config)
# ~30 min before the IT step would, with the full boot log on screen.
# --hostname trinocoordinator makes the baked discovery.uri self-resolve.
run: |
docker run -d --name trino-smoke --hostname trinocoordinator \
apachehudi/hudi-trino_481:latest
ok=""
for i in $(seq 1 18); do
if [ "$(docker inspect -f '{{.State.Running}}' trino-smoke)" != "true" ]; then
echo "trino-smoke container died during startup" >&2
break
fi
if docker exec trino-smoke trino --server localhost:8080 \
--execute "SELECT 1" >/dev/null 2>&1; then
ok=1; echo "Trino answered SELECT 1 (attempt $i)"; break
fi
sleep 10
done
if [ -z "$ok" ]; then
echo "==== trino-smoke boot log ===="
docker logs trino-smoke 2>&1 | tail -200
docker rm -f trino-smoke >/dev/null 2>&1 || true
exit 1
fi
docker rm -f trino-smoke
- name: Set up JDK 17 (restore for the IT run)
# setup-java resets JAVA_HOME on each call; hudi-integ-test needs 17.
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'temurin'
architecture: x64
- name: Run Trino E2E ITs (JDK 17)
run: |
# -DskipITs=false overrides the spark4.0 profile's skipITs=true default
# (see root pom.xml). -Dcompose.profiles=trino starts the profile-gated
# trinocoordinator service; without it every ITTestTrino* class skips.
mvn verify $SCALA_PROFILE -Dspark4.0 -Pintegration-tests \
-pl hudi-integ-test \
-DskipITs=false \
-Ddocker.compose.skip=true \
-Dit.test='ITTestTrino*' \
-Dcompose.profiles=trino \
-Dspark.docker.compose.prefix=$COMPOSE_PREFIX \
$MVN_ARGS
- name: Dump failsafe test outputs on failure
# Surefire redirects test stdout (incl. the streamed trinocoordinator
# boot/query logs) into per-class output files; print their tails so
# server-side failures are readable straight from the workflow log.
if: failure()
run: |
for f in hudi-integ-test/target/failsafe-reports/*-output.txt; do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redirectTestOutputToFile is not set anywhere in this repo and failsafe defaults it to false, so failsafe-reports/*-output.txt never exists and this loop always no-ops. Either add -Dsurefire.redirectTestOutputToFile=true to the IT step or drop the step, since that output already reaches the workflow log.

[ -f "$f" ] || continue
echo "===== $f (last 400 lines) ====="
tail -n 400 "$f"
done
24 changes: 24 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,27 @@ When `--multi-arch` is enabled, the script builds and pushes the amd64 and arm64
Note that `--multi-arch` uses `docker buildx build --push` and the image names in the script are hardcoded to the
`apachehudi/...` Docker Hub repositories, so this flow requires push access to those repositories. No Dockerfile
changes are needed for the current amd64 plus arm64 image set in this repository.

## Trino E2E image - `/trino`

The Trino E2E stack does not use the `hoodie/hadoop` image tree. `docker/trino/` builds
`apachehudi/hudi-trino_<trino-version>` directly on top of the official `trinodb/trino`
image, baking in a locally-assembled native `trino-hudi` plugin directory and the E2E
catalog config (`connector.name=hudi`, metastore at `thrift://hivemetastore:9083`).

This image is built locally on demand (also by the `hudi_trino_e2e.yml` CI workflow) and
is NOT published to Docker Hub. The plugin directory comes from the in-repo shim project
at `docker/trino/shim/` (see `hudi-trino/README.md` for the full build-and-run flow):

```
# JDK 25; hudi-trino must already be installed into the local m2
mvn -f docker/trino/shim/pom.xml clean package -DskipTests
docker/trino/build_image.sh --plugin-dir docker/trino/shim/target/trino-hudi-481
```

The `trinocoordinator` compose service exists only in the
`docker-compose_hadoop340_hive2310_spark402_{amd64,arm64}.yml` pair, behind the `trino`
compose profile, so the default hive-sync flows never start it. For fast plugin
iteration the container supports a bind-mounted overlay: point `TRINO_PLUGIN_DIR` (or
the `-Dtrino.plugin.dir` test property) at a freshly built plugin dir and restart the
container instead of rebuilding the image.
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,29 @@ services:
depends_on:
- minio

# Gated behind the "trino" compose profile: inert for the default hive-sync CI
# rows, only starts when COMPOSE_PROFILES=trino. The plugin overlay defaults to
# docker/trino/empty-overlay (baked-in plugin used); set TRINO_PLUGIN_DIR to a
# locally-built trino-hudi plugin dir to override it at container start.
trinocoordinator:
image: apachehudi/hudi-trino_481:latest
profiles: ["trino"]
hostname: trinocoordinator
container_name: trinocoordinator
ports:
- "8092:8080"
depends_on:
- "hivemetastore"
- "namenode"
links:
- "hivemetastore"
- "hiveserver"
- "hive-metastore-postgresql"
- "namenode"
volumes:
- ${TRINO_PLUGIN_DIR:-${HUDI_WS}/docker/trino/empty-overlay}:/opt/hudi-plugin-overlay:ro
- ${HUDI_WS}:/var/hoodie/ws

volumes:
namenode:
historyserver:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ services:
volumes:
- historyserver:/hadoop/yarn/timeline

# Pure Hive 2.3.10 stack (postgres 2.3 schema -> HMS 2.3.10 HS2 2.3.10).
# Pure Hive 2.3.10 stack (postgres 2.3 schema -> HMS 2.3.10 -> HS2 2.3.10).
# Matches hudi-spark-bundle's compile-time Hive 2.3 client, so Hudi hive-sync
# talks to HMS natively (no Thrift get_table incompat, no sharedPrefixes hack).
# Hadoop 3.4.0 HDFS is backward-compat for the 2.8.4-based Hive client.
Expand Down Expand Up @@ -256,6 +256,29 @@ services:
depends_on:
- minio

# Gated behind the "trino" compose profile: inert for the default hive-sync CI
# rows, only starts when COMPOSE_PROFILES=trino. The plugin overlay defaults to
# docker/trino/empty-overlay (baked-in plugin used); set TRINO_PLUGIN_DIR to a
# locally-built trino-hudi plugin dir to override it at container start.
trinocoordinator:
image: apachehudi/hudi-trino_481:latest
profiles: ["trino"]
hostname: trinocoordinator
container_name: trinocoordinator
ports:
- "8092:8080"
depends_on:
- "hivemetastore"
- "namenode"
links:
- "hivemetastore"
- "hiveserver"
- "hive-metastore-postgresql"
- "namenode"
volumes:
- ${TRINO_PLUGIN_DIR:-${HUDI_WS}/docker/trino/empty-overlay}:/opt/hudi-plugin-overlay:ro
- ${HUDI_WS}:/var/hoodie/ws

volumes:
namenode:
historyserver:
Expand Down
90 changes: 90 additions & 0 deletions docker/demo/sparksql-stock-ticks-trino.commands
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.
*/

// Self-contained COW + MOR stock-ticks seed for the Trino E2E tests
// (ITTestTrinoStockTicks). Mirrors the docker/demo/trino-batch1.commands data
// shape without the Kafka/streaming pipeline, which integ2 does not exercise.
// ts stays STRING so Trino's CSV_UNQUOTED output matches the test's exact
// row assertion: GOOG,2018-08-31 10:29:00,6330,1230.5,1230.5
spark.sql("""
CREATE TABLE stock_ticks_cow (
symbol STRING,
ts STRING,
volume LONG,
open DOUBLE,
close DOUBLE,
dt STRING
) USING hudi
PARTITIONED BY (dt)
LOCATION '/user/hive/warehouse/stock_ticks_cow'
TBLPROPERTIES (
'primaryKey' = 'symbol',
'preCombineField' = 'ts',
'hoodie.datasource.hive_sync.enable' = 'true',
'hoodie.datasource.hive_sync.database' = 'default',
'hoodie.datasource.hive_sync.table' = 'stock_ticks_cow',
'hoodie.datasource.hive_sync.jdbcurl' = 'jdbc:hive2://hiveserver:10000/',
'hoodie.datasource.hive_sync.mode' = 'jdbc',
'hoodie.datasource.hive_sync.partition_fields' = 'dt',
'hoodie.datasource.hive_sync.partition_extractor_class' = 'org.apache.hudi.hive.MultiPartKeysValueExtractor',
'hoodie.datasource.hive_sync.username' = 'hive',
'hoodie.datasource.hive_sync.password' = 'hive'
)
""")

spark.sql("INSERT INTO stock_ticks_cow VALUES ('GOOG', '2018-08-31 10:29:00', 6330, 1230.5, 1230.5, '2018-08-31')")
spark.sql("select symbol, ts, volume, open, close from stock_ticks_cow").show(10, false)
println("STOCK_TICKS_COW_SETUP_SUCCESS")

// MOR variant: identical schema and seed row. 'type' = 'mor' makes hive sync
// register stock_ticks_mor_ro / stock_ticks_mor_rt; the initial insert writes
// parquet base files, so the _ro view already sees the row.
spark.sql("""
CREATE TABLE stock_ticks_mor (
symbol STRING,
ts STRING,
volume LONG,
open DOUBLE,
close DOUBLE,
dt STRING
) USING hudi
PARTITIONED BY (dt)
LOCATION '/user/hive/warehouse/stock_ticks_mor'
TBLPROPERTIES (
'type' = 'mor',
'primaryKey' = 'symbol',
'preCombineField' = 'ts',
'hoodie.datasource.hive_sync.enable' = 'true',
'hoodie.datasource.hive_sync.database' = 'default',
'hoodie.datasource.hive_sync.table' = 'stock_ticks_mor',
'hoodie.datasource.hive_sync.jdbcurl' = 'jdbc:hive2://hiveserver:10000/',
'hoodie.datasource.hive_sync.mode' = 'jdbc',
'hoodie.datasource.hive_sync.partition_fields' = 'dt',
'hoodie.datasource.hive_sync.partition_extractor_class' = 'org.apache.hudi.hive.MultiPartKeysValueExtractor',
'hoodie.datasource.hive_sync.username' = 'hive',
'hoodie.datasource.hive_sync.password' = 'hive'
)
""")

spark.sql("INSERT INTO stock_ticks_mor VALUES ('GOOG', '2018-08-31 10:29:00', 6330, 1230.5, 1230.5, '2018-08-31')")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stock_ticks_mor gets a single INSERT, so it has base files only and the _ro tests traverse the same read path as the COW ones - no MOR-specific signal, and stock_ticks_mor_rt is never queried. One follow-up upsert on the same key plus an _rt assertion would make the MOR row earn its runtime; follow-up, not a blocker.

spark.sql("select symbol, ts, volume, open, close from stock_ticks_mor").show(10, false)
println("STOCK_TICKS_MOR_SETUP_SUCCESS")

// Debug aid: proves stock_ticks_mor_ro / stock_ticks_mor_rt got registered.
spark.sql("show tables").show(100, false)
println("STOCK_TICKS_TRINO_SETUP_SUCCESS")
3 changes: 3 additions & 0 deletions docker/trino/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Transient staging dir populated by build_image.sh (leading slash: must not
# swallow the shim's io/trino/plugin/ source package under shim/).
/plugin/
Loading
Loading