-
Notifications
You must be signed in to change notification settings - Fork 495
feat: intial hudi reg test #3641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,6 +70,21 @@ SPARK_VERSION="3.5.6" | |
|
|
||
| SPARK_SHELL_OPTIONS=("PACKAGE" "JAR") | ||
|
|
||
| # Define test suites to run | ||
| # Each suite specifies: test_file:table_format:test_shortname | ||
| declare -a TEST_SUITES=( | ||
| "spark_sql.sh:delta:spark_sql" | ||
| "spark_hudi.sh:hudi:spark_hudi" | ||
| ) | ||
|
|
||
| # Allow running specific test via environment variable | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can potentially also allow running all suites for a particular format by taking table format as an argument to this script. We can probably do that in a separate PR as an improvement.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets do in another pr |
||
| echo "REGTEST_SUITE=${REGTEST_SUITE}" | ||
| if [[ -n "$REGTEST_SUITE" ]]; then | ||
| echo "Overriding TEST_SUITES to run only: ${REGTEST_SUITE}" | ||
| TEST_SUITES=("${REGTEST_SUITE}") | ||
| fi | ||
| echo "Will run test suites: ${TEST_SUITES[@]}" | ||
|
|
||
| for SCALA_VERSION in "${SCALA_VERSIONS[@]}"; do | ||
| echo "RUN REGRESSION TEST FOR SPARK_MAJOR_VERSION=${SPARK_MAJOR_VERSION}, SPARK_VERSION=${SPARK_VERSION}, SCALA_VERSION=${SCALA_VERSION}" | ||
| # find the project jar | ||
|
|
@@ -89,59 +104,74 @@ for SCALA_VERSION in "${SCALA_VERSIONS[@]}"; do | |
| fi | ||
|
|
||
| for SPARK_SHELL_OPTION in "${SPARK_SHELL_OPTIONS[@]}"; do | ||
| # clean up the default configuration if exists | ||
| if [ -f "${SPARK_HOME}" ]; then | ||
| SPARK_CONF="${SPARK_HOME}/conf/spark-defaults.conf" | ||
| if [ -f ${SPARK_CONF} ]; then | ||
| rm ${SPARK_CONF} | ||
| fi | ||
| fi | ||
|
|
||
| if [ "${SPARK_SHELL_OPTION}" == "PACKAGE" ]; then | ||
| # run the setup without jar configuration | ||
| source ${SCRIPT_DIR}/setup.sh --sparkVersion ${SPARK_VERSION} --scalaVersion ${SCALA_VERSION} --polarisVersion ${POLARIS_VERSION} | ||
| else | ||
| source ${SCRIPT_DIR}/setup.sh --sparkVersion ${SPARK_VERSION} --scalaVersion ${SCALA_VERSION} --polarisVersion ${POLARIS_VERSION} --jar ${JAR_PATH} | ||
| fi | ||
|
|
||
| # run the spark_sql test | ||
| loginfo "Starting test spark_sql.sh" | ||
|
|
||
| TEST_FILE="spark_sql.sh" | ||
| TEST_SHORTNAME="spark_sql" | ||
| TEST_TMPDIR="/tmp/polaris-spark-regtests/${TEST_SHORTNAME}_${SPARK_MAJOR_VERSION}_${SCALA_VERSION}" | ||
| TEST_STDERR="${TEST_TMPDIR}/${TEST_SHORTNAME}.stderr" | ||
| TEST_STDOUT="${TEST_TMPDIR}/${TEST_SHORTNAME}.stdout" | ||
|
|
||
| mkdir -p ${TEST_TMPDIR} | ||
| if (( ${VERBOSE} )); then | ||
| ${SCRIPT_DIR}/${TEST_FILE} 2>${TEST_STDERR} | grep -v 'loading settings' | tee ${TEST_STDOUT} | ||
| else | ||
| ${SCRIPT_DIR}/${TEST_FILE} 2>${TEST_STDERR} | grep -v 'loading settings' > ${TEST_STDOUT} | ||
| fi | ||
| loginfo "Test run concluded for ${TEST_SUITE}:${TEST_SHORTNAME}" | ||
|
|
||
| TEST_REF="$(realpath ${SCRIPT_DIR})/${TEST_SHORTNAME}.ref" | ||
| if cmp --silent ${TEST_STDOUT} ${TEST_REF}; then | ||
| loggreen "Test SUCCEEDED: ${TEST_SUITE}:${TEST_SHORTNAME}" | ||
| else | ||
| logred "Test FAILED: ${TEST_SUITE}:${TEST_SHORTNAME}" | ||
| echo '#!/bin/bash' > ${TEST_TMPDIR}/${TEST_SHORTNAME}.fixdiffs.sh | ||
| echo "meld ${TEST_STDOUT} ${TEST_REF}" >> ${TEST_TMPDIR}/${TEST_SHORTNAME}.fixdiffs.sh | ||
| chmod 750 ${TEST_TMPDIR}/${TEST_SHORTNAME}.fixdiffs.sh | ||
| logred "To compare and fix diffs (if 'meld' installed): ${TEST_TMPDIR}/${TEST_SHORTNAME}.fixdiffs.sh" | ||
| logred "Or manually diff: diff ${TEST_STDOUT} ${TEST_REF}" | ||
| logred "See stderr from test run for additional diagnostics: ${TEST_STDERR}" | ||
| diff ${TEST_STDOUT} ${TEST_REF} | ||
| NUM_FAILURES=$(( NUM_FAILURES + 1 )) | ||
| fi | ||
| # Loop through each test suite | ||
| for TEST_SUITE_CONFIG in "${TEST_SUITES[@]}"; do | ||
| # Parse test suite configuration (format: test_file:table_format:test_shortname) | ||
| IFS=':' read -r TEST_FILE TABLE_FORMAT TEST_SHORTNAME <<< "$TEST_SUITE_CONFIG" | ||
|
|
||
| # Skip this suite if REGTEST_SUITE is set and doesn't match | ||
| if [[ -n "$REGTEST_SUITE" ]] && [[ "$TEST_SUITE_CONFIG" != "$REGTEST_SUITE" ]]; then | ||
| echo "Skipping test suite ${TEST_SHORTNAME} (REGTEST_SUITE=${REGTEST_SUITE})" | ||
| continue | ||
| fi | ||
|
|
||
| loginfo "Setting up for test suite: ${TEST_SHORTNAME} with table format: ${TABLE_FORMAT}" | ||
|
|
||
| # clean up the default configuration if exists | ||
| if [ -f "${SPARK_HOME}" ]; then | ||
| SPARK_CONF="${SPARK_HOME}/conf/spark-defaults.conf" | ||
| if [ -f ${SPARK_CONF} ]; then | ||
| rm ${SPARK_CONF} | ||
| fi | ||
| fi | ||
|
|
||
| # Run setup with appropriate table format | ||
| if [ "${SPARK_SHELL_OPTION}" == "PACKAGE" ]; then | ||
| # run the setup without jar configuration | ||
| source ${SCRIPT_DIR}/setup.sh --sparkVersion ${SPARK_VERSION} --scalaVersion ${SCALA_VERSION} --polarisVersion ${POLARIS_VERSION} --tableFormat ${TABLE_FORMAT} | ||
| else | ||
| source ${SCRIPT_DIR}/setup.sh --sparkVersion ${SPARK_VERSION} --scalaVersion ${SCALA_VERSION} --polarisVersion ${POLARIS_VERSION} --jar ${JAR_PATH} --tableFormat ${TABLE_FORMAT} | ||
| fi | ||
|
|
||
| # run the test | ||
| loginfo "Starting test ${TEST_FILE}" | ||
|
|
||
| TEST_TMPDIR="/tmp/polaris-spark-regtests/${TEST_SHORTNAME}_${SPARK_MAJOR_VERSION}_${SCALA_VERSION}_${SPARK_SHELL_OPTION}" | ||
| TEST_STDERR="${TEST_TMPDIR}/${TEST_SHORTNAME}.stderr" | ||
| TEST_STDOUT="${TEST_TMPDIR}/${TEST_SHORTNAME}.stdout" | ||
|
|
||
| mkdir -p ${TEST_TMPDIR} | ||
| if (( ${VERBOSE} )); then | ||
| ${SCRIPT_DIR}/${TEST_FILE} 2>${TEST_STDERR} | grep -v 'loading settings' | tee ${TEST_STDOUT} | ||
| else | ||
| ${SCRIPT_DIR}/${TEST_FILE} 2>${TEST_STDERR} | grep -v 'loading settings' > ${TEST_STDOUT} | ||
| fi | ||
| loginfo "Test run concluded for ${TEST_SHORTNAME}" | ||
|
|
||
| # Compare output with reference | ||
| TEST_REF="$(realpath ${SCRIPT_DIR})/${TEST_SHORTNAME}.ref" | ||
| if cmp --silent ${TEST_STDOUT} ${TEST_REF}; then | ||
| loggreen "Test SUCCEEDED: ${TEST_SHORTNAME}" | ||
| else | ||
| logred "Test FAILED: ${TEST_SHORTNAME}" | ||
| echo '#!/bin/bash' > ${TEST_TMPDIR}/${TEST_SHORTNAME}.fixdiffs.sh | ||
| echo "meld ${TEST_STDOUT} ${TEST_REF}" >> ${TEST_TMPDIR}/${TEST_SHORTNAME}.fixdiffs.sh | ||
| chmod 750 ${TEST_TMPDIR}/${TEST_SHORTNAME}.fixdiffs.sh | ||
| logred "To compare and fix diffs (if 'meld' installed): ${TEST_TMPDIR}/${TEST_SHORTNAME}.fixdiffs.sh" | ||
| logred "Or manually diff: diff ${TEST_STDOUT} ${TEST_REF}" | ||
| logred "See stderr from test run for additional diagnostics: ${TEST_STDERR}" | ||
| diff ${TEST_STDOUT} ${TEST_REF} | ||
| NUM_FAILURES=$(( NUM_FAILURES + 1 )) | ||
| fi | ||
| done | ||
| done | ||
|
|
||
| # clean up | ||
| if [ "${SPARK_EXISTS}" = "FALSE" ]; then | ||
| rm -rf ${SPARK_HOME} | ||
| export SPARK_HOME="" | ||
| fi | ||
| clean up | ||
| Commented out for faster development/testing - uncomment for CI or final runs | ||
| if [ "${SPARK_EXISTS}" = "FALSE" ]; then | ||
| rm -rf ${SPARK_HOME} | ||
| export SPARK_HOME="" | ||
| fi | ||
| done | ||
|
|
||
| # clean the output dir | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,12 +25,15 @@ | |
| # Warning - it will set the SPARK_HOME environment variable with the spark setup | ||
| # | ||
| # The script can be called independently like following | ||
| # ./setup.sh --sparkVersion ${SPARK_VERSION} --scalaVersion ${SCALA_VERSION} --jar ${JAR_PATH} | ||
| # ./setup.sh --sparkVersion ${SPARK_VERSION} --scalaVersion ${SCALA_VERSION} --jar ${JAR_PATH} --tableFormat ${TABLE_FORMAT} | ||
| # Required Parameters: | ||
| # --sparkVersion : the spark version to setup | ||
| # --scalaVersion : the scala version of spark to setup | ||
| # --jar : path to the local Polaris Spark client jar | ||
| # | ||
| # Optional Parameters: | ||
| # --tableFormat : table format to configure (delta|hudi). Default: delta | ||
| # | ||
|
|
||
| set -x | ||
|
|
||
|
|
@@ -40,6 +43,7 @@ SPARK_VERSION=3.5.6 | |
| SCALA_VERSION=2.12 | ||
| POLARIS_CLIENT_JAR="" | ||
| POLARIS_VERSION="" | ||
| TABLE_FORMAT="delta" | ||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| --sparkVersion) | ||
|
|
@@ -62,13 +66,24 @@ while [[ $# -gt 0 ]]; do | |
| shift # past argument | ||
| shift # past value | ||
| ;; | ||
| --tableFormat) | ||
| TABLE_FORMAT="$2" | ||
| shift # past argument | ||
| shift # past value | ||
| ;; | ||
| --) shift; | ||
| break | ||
| ;; | ||
| esac | ||
| done | ||
|
|
||
| echo "SET UP FOR SPARK_VERSION=${SPARK_VERSION} SCALA_VERSION=${SCALA_VERSION} POLARIS_VERSION=${POLARIS_VERSION} POLARIS_CLIENT_JAR=${POLARIS_CLIENT_JAR}" | ||
| echo "SET UP FOR SPARK_VERSION=${SPARK_VERSION} SCALA_VERSION=${SCALA_VERSION} POLARIS_VERSION=${POLARIS_VERSION} POLARIS_CLIENT_JAR=${POLARIS_CLIENT_JAR} TABLE_FORMAT=${TABLE_FORMAT}" | ||
|
|
||
| # Validate table format | ||
| if [[ "$TABLE_FORMAT" != "delta" && "$TABLE_FORMAT" != "hudi" ]]; then | ||
| echo "Error: Invalid table format '${TABLE_FORMAT}'. Must be 'delta' or 'hudi'." | ||
| exit 1 | ||
| fi | ||
|
|
||
| if [ "$SCALA_VERSION" == "2.12" ]; then | ||
| SPARK_DISTRIBUTION=spark-${SPARK_VERSION}-bin-hadoop3 | ||
|
|
@@ -141,14 +156,33 @@ else | |
| if [[ -z "$POLARIS_CLIENT_JAR" ]]; then | ||
| cat << EOF >> ${SPARK_CONF} | ||
| # POLARIS Spark client test conf | ||
| EOF | ||
| if [[ "$TABLE_FORMAT" == "hudi" ]]; then | ||
| cat << EOF >> ${SPARK_CONF} | ||
| spark.jars.packages org.apache.polaris:polaris-spark-3.5_$SCALA_VERSION:$POLARIS_VERSION | ||
| # Note: Hudi package is passed via --packages on command line in spark_hudi.sh | ||
| # to ensure it's resolved before Kryo initialization | ||
| EOF | ||
| else | ||
| cat << EOF >> ${SPARK_CONF} | ||
| spark.jars.packages org.apache.polaris:polaris-spark-3.5_$SCALA_VERSION:$POLARIS_VERSION,io.delta:delta-spark_${SCALA_VERSION}:3.2.1 | ||
| EOF | ||
| fi | ||
| else | ||
| cat << EOF >> ${SPARK_CONF} | ||
| # POLARIS Spark client test conf | ||
| spark.jars $POLARIS_CLIENT_JAR | ||
| EOF | ||
| if [[ "$TABLE_FORMAT" == "hudi" ]]; then | ||
| cat << EOF >> ${SPARK_CONF} | ||
| # Note: Hudi package is passed via --packages on command line in spark_hudi.sh | ||
| # to ensure it's resolved before Kryo initialization | ||
| EOF | ||
| else | ||
| cat << EOF >> ${SPARK_CONF} | ||
| spark.jars.packages io.delta:delta-spark_${SCALA_VERSION}:3.2.1 | ||
| EOF | ||
| fi | ||
| fi | ||
|
|
||
| cat << EOF >> ${SPARK_CONF} | ||
|
|
@@ -157,9 +191,26 @@ spark.sql.variable.substitute true | |
|
|
||
| spark.driver.extraJavaOptions -Dderby.system.home=${DERBY_HOME} | ||
|
|
||
| EOF | ||
|
|
||
| if [[ "$TABLE_FORMAT" == "hudi" ]]; then | ||
| cat << EOF >> ${SPARK_CONF} | ||
| spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions,org.apache.spark.sql.hudi.HoodieSparkSessionExtension | ||
| # this configuration is needed for hudi table | ||
| spark.sql.catalog.spark_catalog=org.apache.spark.sql.hudi.catalog.HoodieCatalog | ||
| spark.serializer=org.apache.spark.serializer.KryoSerializer | ||
| spark.kryo.registrator=org.apache.spark.HoodieSparkKryoRegistrar | ||
| hoodie.metadata.enable=false | ||
| EOF | ||
| else | ||
| cat << EOF >> ${SPARK_CONF} | ||
| spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions,io.delta.sql.DeltaSparkSessionExtension | ||
| # this configuration is needed for delta table | ||
| spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog | ||
| EOF | ||
| fi | ||
|
|
||
| cat << EOF >> ${SPARK_CONF} | ||
| spark.sql.catalog.polaris=org.apache.polaris.spark.SparkCatalog | ||
| spark.sql.catalog.polaris.uri=http://${POLARIS_HOST:-localhost}:8181/api/catalog | ||
| # this configuration is currently only used for iceberg tables, generic tables currently | ||
|
|
@@ -182,7 +233,12 @@ fi | |
| echo "Launch spark-sql at ${SPARK_HOME}/bin/spark-sql" | ||
| # bootstrap dependencies so that future queries don't need to wait for the downloads. | ||
| # this is mostly useful for building the Docker image with all needed dependencies | ||
| ${SPARK_HOME}/bin/spark-sql -e "SELECT 1" | ||
| if [[ "$TABLE_FORMAT" == "hudi" ]]; then | ||
| # For Hudi: Pass --packages on command line to match official Hudi docs approach | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i don't think we need the if else here anymore |
||
| ${SPARK_HOME}/bin/spark-sql --packages org.apache.hudi:hudi-spark3.5-bundle_${SCALA_VERSION}:1.1.1 -e "SELECT 1" | ||
| else | ||
| ${SPARK_HOME}/bin/spark-sql -e "SELECT 1" | ||
| fi | ||
|
|
||
| # ensure SPARK_HOME is setup for later tests | ||
| export SPARK_HOME=$SPARK_HOME | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| {"defaults":{"default-base-location":"file:///tmp/spark_hudi_catalog"},"overrides":{"prefix":"spark_hudi_catalog"},"endpoints":["GET /v1/{prefix}/namespaces","GET /v1/{prefix}/namespaces/{namespace}","HEAD /v1/{prefix}/namespaces/{namespace}","POST /v1/{prefix}/namespaces","POST /v1/{prefix}/namespaces/{namespace}/properties","DELETE /v1/{prefix}/namespaces/{namespace}","GET /v1/{prefix}/namespaces/{namespace}/tables","GET /v1/{prefix}/namespaces/{namespace}/tables/{table}","HEAD /v1/{prefix}/namespaces/{namespace}/tables/{table}","POST /v1/{prefix}/namespaces/{namespace}/tables","POST /v1/{prefix}/namespaces/{namespace}/tables/{table}","DELETE /v1/{prefix}/namespaces/{namespace}/tables/{table}","POST /v1/{prefix}/tables/rename","POST /v1/{prefix}/namespaces/{namespace}/register","POST /v1/{prefix}/namespaces/{namespace}/tables/{table}/metrics","POST /v1/{prefix}/transactions/commit","GET /v1/{prefix}/namespaces/{namespace}/views","GET /v1/{prefix}/namespaces/{namespace}/views/{view}","HEAD /v1/{prefix}/namespaces/{namespace}/views/{view}","POST /v1/{prefix}/namespaces/{namespace}/views","POST /v1/{prefix}/namespaces/{namespace}/views/{view}","DELETE /v1/{prefix}/namespaces/{namespace}/views/{view}","POST /v1/{prefix}/views/rename","GET polaris/v1/{prefix}/namespaces/{namespace}/generic-tables","POST polaris/v1/{prefix}/namespaces/{namespace}/generic-tables","DELETE polaris/v1/{prefix}/namespaces/{namespace}/generic-tables/{generic-table}","GET polaris/v1/{prefix}/namespaces/{namespace}/generic-tables/{generic-table}","GET /polaris/v1/{prefix}/namespaces/{namespace}/policies","POST /polaris/v1/{prefix}/namespaces/{namespace}/policies","GET /polaris/v1/{prefix}/namespaces/{namespace}/policies/{policy-name}","PUT /polaris/v1/{prefix}/namespaces/{namespace}/policies/{policy-name}","DELETE /polaris/v1/{prefix}/namespaces/{namespace}/policies/{policy-name}","PUT /polaris/v1/{prefix}/namespaces/{namespace}/policies/{policy-name}/mappings","POST /polaris/v1/{prefix}/namespaces/{namespace}/policies/{policy-name}/mappings","GET /polaris/v1/{prefix}/applicable-policies"]} | ||
| Catalog created | ||
| spark-sql (default)> use polaris; | ||
| spark-sql ()> create namespace hudi_db1; | ||
| spark-sql ()> create namespace hudi_db2; | ||
| spark-sql ()> show namespaces; | ||
| hudi_db1 | ||
| hudi_db2 | ||
| spark-sql ()> | ||
| > create namespace hudi_db1.schema1; | ||
| spark-sql ()> show namespaces in hudi_db1; | ||
| hudi_db1.schema1 | ||
| spark-sql ()> | ||
| > create table hudi_db1.schema1.hudi_tb1 (id int, name string) using hudi location 'file:///tmp/spark_hudi_catalog/hudi_tb1'; | ||
| spark-sql ()> show tables in hudi_db1; | ||
| spark-sql ()> show tables in hudi_db1.schema1; | ||
| spark-sql ()> | ||
| > use hudi_db1.schema1; | ||
| spark-sql (hudi_db1.schema1)> insert into hudi_tb1 values (1, 'alice'), (2, 'bob'); | ||
| spark-sql (hudi_db1.schema1)> select * from hudi_tb1 order by id; | ||
| spark-sql (hudi_db1.schema1)> | ||
| > create table hudi_tb2 (name string, age int, country string) using hudi partitioned by (country) location 'file:///tmp/spark_hudi_catalog/hudi_tb2'; | ||
| spark-sql (hudi_db1.schema1)> insert into hudi_tb2 values ('anna', 10, 'US'), ('james', 32, 'US'), ('yan', 16, 'CHINA'); | ||
| spark-sql (hudi_db1.schema1)> select name, country from hudi_tb2 order by age; | ||
| spark-sql (hudi_db1.schema1)> | ||
| > show tables; | ||
| spark-sql (hudi_db1.schema1)> | ||
| > use hudi_db1; | ||
| spark-sql (hudi_db1)> create table iceberg_tb (col1 int); | ||
| spark-sql (hudi_db1)> insert into iceberg_tb values (100), (200); | ||
| spark-sql (hudi_db1)> select * from iceberg_tb order by col1; | ||
| 100 | ||
| 200 | ||
| spark-sql (hudi_db1)> | ||
| > show tables; | ||
| iceberg_tb | ||
| spark-sql (hudi_db1)> show tables in hudi_db1.schema1; | ||
| spark-sql (hudi_db1)> | ||
| > drop table hudi_db1.schema1.hudi_tb1; | ||
| spark-sql (hudi_db1)> drop table hudi_db1.schema1.hudi_tb2; | ||
| spark-sql (hudi_db1)> drop namespace hudi_db1.schema1; | ||
| spark-sql (hudi_db1)> drop table iceberg_tb; | ||
| spark-sql (hudi_db1)> drop namespace hudi_db1; | ||
| spark-sql (hudi_db1)> drop namespace hudi_db2; | ||
| spark-sql (hudi_db1)> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about let's enforce the test file name to the format like xxx_<table_format>.sh, and have a separate folder to include all test src file and reference file. Then we just need to list the folder to get all test files, and extract the table format by parsing the file name. The benefit would be easy to onboard new tests, and developer doesn't have to input a long string when running single test (just the file name)