[#8771] feat(spark-connector): Add Spark 4.0 support, drop 3.3 and 3.4 - #12414
Conversation
b258afc to
5923cfc
Compare
Code Coverage Report
Files |
|
Thank you @roryqi |
|
Since we are going to refactor this part, I think the architecture should follow two principles:
The tests should follow the same structure as well. This will keep each Spark version independent and make it much easier to deprecate or remove old versions in the future. |
|
@diqiu50 Thanks for your suggestions. I'll take a look and get back to you later. |
|
Agreed on both principles, and I am happy to take the refactor as a separate PR ahead of this one, then rebase this one on top. It changes how all of 3.x builds, so it deserves review on its own. Below is what I found while sizing it, and a layout to check against before I start. Where the version boundaries actually fall. I tried compiling Three different boundaries means the layout has to be additive
I checked the pieces that make The shared tests are the larger half. All four version modules consume For scale, the shared sources end up compiled once per supported (Spark, Scala) pair after the refactor: 3.3/3.4/3.5 under 2.12 and 2.13, plus 4.0 under 2.13, with the 2.13 ones excluding the Paimon subset. One limit worth stating: the Iceberg catalog subclasses cannot fully follow principle 2. Questions:
|
|
friendly ping @diqiu50 |
|
We could consider supporting Spark 4.1 instead of 4.0. Spark 4.1 is more stable. |
|
No problem, I can adjust it to add support for Spark 4.1. Do you have any feedback on the three questions I raised earlier? Thanks |
|
However, my company’s business are still running on Spark 4.0.x. Is it feasible to support both Spark 4.0 and 4.1 simultaneously? I can submit multiple patches to achieve compatibility with both versions. @diqiu50 also cc @jerryshao |
|
I think we can simplify the layout further. First, we don't need to keep Spark 3.3 support, so there is no need to introduce a Second, I don't think Conceptually: The tests should follow the same model: shared test sources should be compiled separately for each Spark version rather than distributed as a precompiled The Glue download task and similar build logic can be moved out separately. I don't think build-time utilities are a strong enough reason to keep I'm also fine with a reasonable amount of code duplication between version-specific implementations. Keeping clear version boundaries, avoiding cross-version dependencies, and making each supported Spark version independently removable are more important than eliminating every duplicated line of code. |
|
Thanks for your feedback. @diqiu50 |
|
Thanks @LuciferYang |
Alright, let me evaluate it against this rule first. |
|
Hi @LuciferYang , we also discussed offline. I think we can support Spark 4.0 along with 4.1. But I don't want to maintain too many Spark versions. So I want to drop support for Spark 3.4, WDYT? Also cc @diqiu50 |
|
Agreed. Spark's versioning policy keeps 3.5.x on extended LTS through November 2027, while 3.3 and 3.4 are past their maintenance windows: the last releases were 3.3.4 and 3.4.4 (October 2024). This also answers the duplication question @diqiu50 raised. Under the strict one-directory-per-version layout, dropping 3.4 takes duplicated lines from 873 to 285, and the Final matrix: 3.5, 4.0, 4.1. One note: this stops publishing |
db1654b to
1a55481
Compare
|
I suggest we start by supporting two versions. That will give us a clearer picture of the feasibility of this approach and the maintenance cost involved. |
Got it, I'll combine my local PR1 and PR2 into a single submission here, and ping you once the code is ready. |
…he 3.x line Spark 3.3 and 3.4 are past their maintenance windows, so 3.5 is the only 3.x line that will see another release. Dropping them lets the version-specific converter and catalog subclasses collapse back into spark-common. Stops publishing gravitino-spark-connector-runtime-3.3 and -3.4.
…ared source set spark-common stops being a Gradle module. Each version module composes its own source set from spark-common/src/main/java, spark-common/src/main/spark<NN> and its own src/main/java, so the shared code is compiled against every supported Spark version rather than once. The test tree works the same way, replacing the testArtifacts jar. Stops publishing gravitino-spark-common.
1a55481 to
b5f71dd
Compare
Adds v4.0/spark and v4.0/spark-runtime, publishing gravitino-spark-4.0_2.13 and gravitino-spark-connector-runtime-4.0_2.13. Spark 4 is Scala 2.13 only and needs JDK 17. Moves SparkIcebergTable and SparkJdbcTable back to the shared tree: both compile unchanged on 4.0, so the spark35 copies were duplication. Only the authorization parser needs a per-version flavor.
…xclusion lists The spark-connector ITs are meant to run only in the dedicated Spark IT workflow; every other workflow excludes them. v4.0 was added without being added to those lists, so its ITs also ran inside the backend IT job, where -PjdbcBackend=mysql makes two suites drop and recreate the same MYSQL_JDBC_BACKEND database out from under each other. Excluded alongside 3.5 in backend IT and in the two Trino workflows. Also raises the build job cap from 90 to 120 minutes. It has been finishing in 75-84 minutes and :core:test alone has varied from 27 to 45 minutes across runs on identical code, so the margin was already thinner than the runner's own variance.
|
CI passed, Please review it when you have time, and I will revise it if there are any issues. Thanks @diqiu50 |
|
The Spark version is already fixed by the jar on the classpath, so CatalogNameAdaptor reading SPARK_VERSION and looking the catalog classes up again is a second dispatch on the same information — and it costs us 11 hardcoded class names that nothing checks, plus a misleading provider is not supported yet warning when the jar does not match the cluster. Could each version module declare its own catalog classes and pass them to the driver plugin, instead of the shared code looking them up by version? Then the names are checked at compile time, CatalogNameAdaptor and the version package go away, and adding a Spark version stops touching shared code. |
…are its own catalogs CatalogNameAdaptor read SPARK_VERSION at runtime and looked catalog classes up by string, a second dispatch on information the jar on the classpath already fixes. It cost 11 hardcoded class names that nothing checked and a misleading "provider is not supported yet" warning when the jar did not match the cluster. Each version module now declares a SparkCatalogs class at the same FQN, holding compile-time class references, so the driver plugin reads the table from whichever jar is present. Provider to catalog-kind mapping is the same on every version, so that stays shared as SparkCatalogKind. Adding a Spark version no longer touches shared code.
|
Done in e321b66. Agreed it was a second dispatch, and the version package is gone. Each version module now declares its own One entry cannot be a class reference: Paimon publishes no
Verified: unit tests at 123 cases on 3.5 Scala 2.12, 120 on 2.13, 120 on 4.0; |
|
friendly ping @diqiu50 |
There was a problem hiding this comment.
Pull request overview
Adds Spark 4.0 connector support while narrowing the supported matrix to Spark 3.5 and 4.0.
Changes:
- Compiles shared connector sources and tests independently for each Spark version.
- Adds Spark 4.0 catalogs, runtime packaging, compatibility adaptations, and CI coverage.
- Removes Spark 3.3/3.4 modules and consolidates their remaining behavior into shared code.
Reviewed changes
Copilot reviewed 102 out of 109 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
spark-connector/v4.0/spark/src/test/resources/log4j2.properties |
Adds Spark 4 test logging. |
spark-connector/v4.0/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/jdbc/SparkJdbcPostgreSqlCatalogIT40.java |
Adds Spark 4 PostgreSQL IT. |
spark-connector/v4.0/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/jdbc/SparkJdbcMysqlCatalogIT40.java |
Adds Spark 4 MySQL IT. |
spark-connector/v4.0/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/iceberg/SparkIcebergCatalogHiveBackendIT40.java |
Adds Spark 4 Iceberg IT adaptations. |
spark-connector/v4.0/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/hive/SparkHiveCatalogIT40.java |
Adds Spark 4 Hive IT. |
spark-connector/v4.0/spark/src/main/java/org/apache/gravitino/spark/connector/jdbc/postgresql/GravitinoPostgreSqlCatalogSpark40.java |
Adds Spark 4 PostgreSQL catalog. |
spark-connector/v4.0/spark/src/main/java/org/apache/gravitino/spark/connector/jdbc/GravitinoJdbcCatalogSpark40.java |
Adds Spark 4 JDBC catalog. |
spark-connector/v4.0/spark/src/main/java/org/apache/gravitino/spark/connector/iceberg/GravitinoIcebergCatalogSpark40.java |
Implements Spark 4 Iceberg procedures. |
spark-connector/v4.0/spark/src/main/java/org/apache/gravitino/spark/connector/hive/GravitinoHiveCatalogSpark40.java |
Adds Spark 4 Hive catalog. |
spark-connector/v4.0/spark/src/main/java/org/apache/gravitino/spark/connector/glue/GravitinoGlueCatalogSpark40.java |
Adds Spark 4 Glue catalog. |
spark-connector/v4.0/spark/src/main/java/org/apache/gravitino/spark/connector/catalog/SparkCatalogs.java |
Declares Spark 4 catalog implementations. |
spark-connector/v4.0/spark/build.gradle.kts |
Configures Spark 4 sources, dependencies, and tests. |
spark-connector/v4.0/spark-runtime/build.gradle.kts |
Packages the Spark 4 runtime artifact. |
spark-connector/v3.5/spark/src/test/java/org/apache/gravitino/spark/connector/version/TestCatalogNameAdaptor.java |
Removes obsolete version-name tests. |
spark-connector/v3.5/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/authorization/SparkAuthorizationIT35.java |
Simplifies Spark 3.5 authorization IT. |
spark-connector/v3.5/spark/src/main/java/org/apache/gravitino/spark/connector/paimon/GravitinoPaimonCatalogSpark35.java |
Uses the consolidated Paimon base. |
spark-connector/v3.5/spark/src/main/java/org/apache/gravitino/spark/connector/jdbc/GravitinoJdbcCatalogSpark35.java |
Uses the consolidated JDBC base. |
spark-connector/v3.5/spark/src/main/java/org/apache/gravitino/spark/connector/iceberg/GravitinoIcebergCatalogSpark35.java |
Moves Spark 3.5 procedure integration here. |
spark-connector/v3.5/spark/src/main/java/org/apache/gravitino/spark/connector/hive/GravitinoHiveCatalogSpark35.java |
Uses the consolidated Hive base. |
spark-connector/v3.5/spark/src/main/java/org/apache/gravitino/spark/connector/glue/GravitinoGlueCatalogSpark35.java |
Uses the consolidated Glue base. |
spark-connector/v3.5/spark/src/main/java/org/apache/gravitino/spark/connector/catalog/SparkCatalogs.java |
Declares Spark 3.5 catalog implementations. |
spark-connector/v3.5/spark/build.gradle.kts |
Compiles shared sources directly for Spark 3.5. |
spark-connector/v3.4/spark/src/test/resources/log4j2.properties |
Removes Spark 3.4 test logging. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/version/TestCatalogNameAdaptor.java |
Removes Spark 3.4 catalog-name tests. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/TestSparkTypeConverter34.java |
Removes superseded converter tests. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/TestSparkTableChangeConverter34.java |
Removes superseded table-change tests. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/sql/SparkSQLRegressionTest34.java |
Removes Spark 3.4 SQL tests. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/paimon/SparkPaimonCatalogFilesystemBackendIT34.java |
Removes Spark 3.4 Paimon IT. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/iceberg/SparkIcebergCatalogRestBackendIT34.java |
Removes Spark 3.4 Iceberg REST IT. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/iceberg/SparkIcebergCatalogHiveBackendIT34.java |
Removes Spark 3.4 Iceberg Hive IT. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/glue/SparkAwsGlueCatalogIT34.java |
Removes Spark 3.4 Glue IT. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/authorization/SparkJwksAuthorizationIT34.java |
Removes Spark 3.4 JWKS IT. |
spark-connector/v3.4/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/authorization/SparkAuthorizationIT34.java |
Removes Spark 3.4 authorization IT. |
spark-connector/v3.4/spark/src/main/java/org/apache/gravitino/spark/connector/SparkTypeConverter34.java |
Removes version-specific type conversion. |
spark-connector/v3.4/spark/src/main/java/org/apache/gravitino/spark/connector/SparkTableChangeConverter34.java |
Removes version-specific table-change conversion. |
spark-connector/v3.4/spark/src/main/java/org/apache/gravitino/spark/connector/paimon/GravitinoPaimonCatalogSpark34.java |
Removes Spark 3.4 Paimon catalog. |
spark-connector/v3.4/spark/src/main/java/org/apache/gravitino/spark/connector/jdbc/SparkJdbcTypeConverter34.java |
Removes Spark 3.4 JDBC converter. |
spark-connector/v3.4/spark/src/main/java/org/apache/gravitino/spark/connector/jdbc/GravitinoJdbcCatalogSpark34.java |
Removes Spark 3.4 JDBC catalog. |
spark-connector/v3.4/spark/src/main/java/org/apache/gravitino/spark/connector/iceberg/GravitinoIcebergCatalogSpark34.java |
Removes Spark 3.4 Iceberg catalog. |
spark-connector/v3.4/spark/src/main/java/org/apache/gravitino/spark/connector/hive/SparkHiveTypeConverter34.java |
Removes Spark 3.4 Hive converter. |
spark-connector/v3.4/spark/src/main/java/org/apache/gravitino/spark/connector/hive/GravitinoHiveCatalogSpark34.java |
Removes Spark 3.4 Hive catalog. |
spark-connector/v3.4/spark/src/main/java/org/apache/gravitino/spark/connector/glue/GravitinoGlueCatalogSpark34.java |
Removes Spark 3.4 Glue catalog. |
spark-connector/v3.4/build.gradle.kts |
Removes Spark 3.4 grouping configuration. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/version/TestCatalogNameAdaptor.java |
Removes Spark 3.3 catalog-name tests. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/sql/SparkSQLRegressionTest33.java |
Removes Spark 3.3 SQL tests. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/paimon/SparkPaimonCatalogFilesystemBackendIT33.java |
Removes Spark 3.3 Paimon IT. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/jdbc/SparkJdbcPostgreSqlCatalogIT33.java |
Removes Spark 3.3 PostgreSQL IT. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/jdbc/SparkJdbcMysqlCatalogIT33.java |
Removes Spark 3.3 MySQL IT. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/iceberg/SparkIcebergCatalogRestBackendIT33.java |
Removes Spark 3.3 Iceberg REST IT. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/hive/SparkHiveCatalogIT33.java |
Removes Spark 3.3 Hive IT. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/glue/SparkAwsGlueCatalogIT33.java |
Removes Spark 3.3 Glue IT. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/authorization/SparkJwksAuthorizationIT33.java |
Removes Spark 3.3 JWKS IT. |
spark-connector/v3.3/spark/src/test/java/org/apache/gravitino/spark/connector/integration/test/authorization/SparkAuthorizationIT33.java |
Removes Spark 3.3 authorization IT. |
spark-connector/v3.3/spark/src/main/java/org/apache/gravitino/spark/connector/paimon/GravitinoPaimonCatalogSpark33.java |
Removes Spark 3.3 Paimon catalog. |
spark-connector/v3.3/spark/src/main/java/org/apache/gravitino/spark/connector/jdbc/postgresql/GravitinoPostgreSqlCatalogSpark33.java |
Removes Spark 3.3 PostgreSQL catalog. |
spark-connector/v3.3/spark/src/main/java/org/apache/gravitino/spark/connector/iceberg/GravitinoIcebergCatalogSpark33.java |
Removes Spark 3.3 Iceberg catalog. |
spark-connector/v3.3/spark/build.gradle.kts |
Removes Spark 3.3 connector build. |
spark-connector/v3.3/spark-runtime/build.gradle.kts |
Removes Spark 3.3 runtime build. |
spark-connector/v3.3/build.gradle.kts |
Removes Spark 3.3 grouping configuration. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/TestSparkTypeConverter.java |
Tests shared timestamp-NTZ conversion. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/plugin/TestGravitinoDriverPlugin.java |
Tests conditional Paimon extension registration. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/integration/test/util/SparkUtilIT.java |
Updates cross-version test guidance. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/integration/test/util/SparkTableInfo.java |
Suppresses cross-version schema deprecation. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/integration/test/sql/SparkSQLRegressionTest.java |
Updates regression-test command. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/integration/test/jdbc/SparkJdbcPostgreSqlCatalogIT.java |
Makes the shared PostgreSQL IT abstract. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/integration/test/iceberg/SparkIcebergCatalogIT.java |
Uses Spark-compatible literal construction. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/integration/test/hive/SparkHiveCatalogIT.java |
Tests Spark's plural partition exception. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/integration/test/authorization/SparkAuthorizationIT.java |
Consolidates write-privilege assertions. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/hive/TestSparkHiveTypeConverter.java |
Expands shared Hive conversion tests. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/catalog/TestTransformTableChange.java |
Tests shared default-value conversion. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/catalog/TestSparkCatalogs.java |
Validates per-version catalog declarations. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/catalog/TestSparkCatalogKind.java |
Tests provider-to-catalog mapping. |
spark-connector/spark-common/src/test/java/org/apache/gravitino/spark/connector/authorization/TestRequiredPrivilegesSparkResolution.java |
Handles cross-version API deprecation. |
spark-connector/spark-common/src/main/spark40/org/apache/gravitino/spark/connector/authorization/GravitinoAuthorizationSparkSessionExtensions.java |
Adds the Spark 4 authorization parser flavor. |
spark-connector/spark-common/src/main/spark35/org/apache/gravitino/spark/connector/paimon/SparkPaimonTable.java |
Restricts Paimon table code to Spark 3.5. |
spark-connector/spark-common/src/main/spark35/org/apache/gravitino/spark/connector/paimon/PaimonPropertiesConverter.java |
Restricts Paimon property conversion to Spark 3.5. |
spark-connector/spark-common/src/main/spark35/org/apache/gravitino/spark/connector/paimon/PaimonPropertiesConstants.java |
Restricts Paimon constants to Spark 3.5. |
spark-connector/spark-common/src/main/spark35/org/apache/gravitino/spark/connector/paimon/GravitinoPaimonCatalog.java |
Restricts the Paimon catalog to Spark 3.5. |
spark-connector/spark-common/src/main/spark35/org/apache/gravitino/spark/connector/authorization/GravitinoAuthorizationSparkSessionExtensions.java |
Adds the Spark 3.5 authorization parser flavor. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/version/CatalogNameAdaptor.java |
Removes runtime Spark-version catalog lookup. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/utils/SparkPartitionUtils.java |
Preserves char/varchar types on Spark 4. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/utils/HiveGravitinoOperationOperator.java |
Adopts the plural Spark partition exception. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/SparkTypeConverter.java |
Consolidates NTZ and char/varchar conversion. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/SparkTableChangeConverter.java |
Consolidates column-default conversion. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/plugin/GravitinoDriverPlugin.java |
Uses per-module catalog declarations. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/jdbc/SparkJdbcTypeConverter.java |
Consolidates JDBC timestamp handling. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/iceberg/GravitinoIcebergCatalog.java |
Moves procedure APIs to version modules. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/hive/SparkHiveTypeConverter.java |
Consolidates Hive timestamp conversion. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/hive/SparkHiveTable.java |
Updates partition exception signature. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/catalog/SparkCatalogKind.java |
Introduces shared provider classification. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/catalog/BaseCatalog.java |
Centralizes write-privilege table loading. |
spark-connector/spark-common/src/main/java/org/apache/gravitino/spark/connector/authorization/AuthorizationTable.java |
Handles schema API deprecation. |
spark-connector/spark-common/build.gradle.kts |
Removes the shared Gradle module. |
spark-connector/build.gradle.kts |
Owns shared formatting and Glue test assets. |
settings.gradle.kts |
Registers only Spark 3.5 and 4.0 modules. |
gradle/libs.versions.toml |
Adds Spark 4 and Iceberg 4.0 versions. |
docs/spark-connector/spark-integration-test.md |
Updates supported test commands. |
docs/spark-connector/spark-connector.md |
Documents the new support matrix. |
docs/spark-connector/spark-catalog-paimon.md |
Documents Spark 4's Paimon limitation. |
docs/spark-connector/spark-catalog-iceberg.md |
Adds Spark 4 Iceberg artifacts. |
docs/spark-connector/spark-catalog-glue.md |
Narrows verified Glue support. |
docs/how-to-build.md |
Updates connector build instructions. |
build.gradle.kts |
Assigns Spark 4 modules a JDK 17 target. |
.github/workflows/trino-multi-version-test.yml |
Updates excluded Spark modules. |
.github/workflows/trino-integration-test.yml |
Updates excluded Spark modules. |
.github/workflows/spark-integration-test-action.yml |
Runs Spark 3.5 and 4.0 integration tests. |
.github/workflows/cron-integration-test.yml |
Uploads Spark 4 integration logs. |
.github/workflows/build.yml |
Updates Scala builds and timeout. |
.github/workflows/backend-integration-test-action.yml |
Updates excluded Spark test modules. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| } catch (ClassNotFoundException e) { | ||
| return false; | ||
| } |
| throw new RuntimeException("Failed to load Iceberg Procedure " + identifier, e); | ||
| } | ||
|
|
||
| throw new RuntimeException("Procedure does not exist: " + identifier); |
| ## Usage | ||
|
|
||
| 1. [Build](../how-to-build.md) or download the package ([gravitino-spark-connector-runtime-3.3](https://mvnrepository.com/artifact/org.apache.gravitino/gravitino-spark-connector-runtime-3.3), [gravitino-spark-connector-runtime-3.4](https://mvnrepository.com/artifact/org.apache.gravitino/gravitino-spark-connector-runtime-3.4), [gravitino-spark-connector-runtime-3.5](https://mvnrepository.com/artifact/org.apache.gravitino/gravitino-spark-connector-runtime-3.5)), and place it to the classpath of Spark. | ||
| 1. [Build](../how-to-build.md) or download the package matching your Spark minor version ([gravitino-spark-connector-runtime-3.5](https://mvnrepository.com/artifact/org.apache.gravitino/gravitino-spark-connector-runtime-3.5), [gravitino-spark-connector-runtime-4.0](https://mvnrepository.com/artifact/org.apache.gravitino/gravitino-spark-connector-runtime-4.0)), and place it to the classpath of Spark. |
| private static final Logger LOG = LoggerFactory.getLogger(HiveGravitinoOperationOperator.class); | ||
|
|
||
| private org.apache.gravitino.rel.Table gravitinoTable; | ||
| private static final String PARTITION_NAME_DELIMITER = "/"; | ||
| private static final String PARTITION_VALUE_DELIMITER = "="; |
|
An alternative that keeps the SparkCatalogKind split: move GravitinoSparkPlugin into the version modules and have it build a bindings object to pass to GravitinoDriverPlugin. The FQN stays the same, so spark.plugins is unchanged for users; the bindings are keyed by SparkCatalogKind and built from class literals, with Paimon as a string for the reason it already is. The dependency then runs version module → shared code — ordinary Java, with no import that resolves only after source-set composition. Three things it buys:
Cost is a ~15-line GravitinoSparkPlugin per version module and one constructor argument on GravitinoDriverPlugin. |
| * under the License. | ||
| */ | ||
| import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar | ||
|
|
There was a problem hiding this comment.
Two suggestions on the build layout
Keep spark-common as a Gradle module. No need to publish it or depend on it as a jar — the srcDirs composition can stay as is — but as a module the shared tree still compiles on its own, the way trino-connector:trino-connector does. It also gives Spotless and Javadoc a natural home instead of the special-casing in spark-connector/build.gradle.kts.
Factor the common build config into a reusable plugin. srcDirs carries sources, not dependencies, so the module alone does not remove the duplication: the dependencies {} blocks in v3.5/spark and v4.0/spark are ~140 lines each and differ by about a dozen. The common part could move into a convention plugin both apply.
| * that Spark 4 added the abstract {@code parseRoutineParam} method to {@code ParserInterface}. | ||
| */ | ||
| public class GravitinoAuthorizationSparkSessionExtensions | ||
| implements Function1<SparkSessionExtensions, Void> { |
There was a problem hiding this comment.
I suggest extracting the reusable parts into a base class and placing it in the shared module.
| if (!dest.exists()) { | ||
| logger.lifecycle("Downloading $jarName ...") | ||
| URI(downloadUrl).toURL().openStream().use { input -> | ||
| dest.outputStream().use { output -> input.copyTo(output) } |
There was a problem hiding this comment.
Move these to spark common is better
| ":spark-connector:spark-4.0", | ||
| ":spark-connector:spark-runtime-4.0" | ||
| ) | ||
|
|
There was a problem hiding this comment.
I suggest configuring it in the specific module rather than at the top level. Please refer to the Trino implementation.
| if (!SparkCatalogs.classNames().containsKey(SparkCatalogKind.LAKEHOUSE_PAIMON)) { | ||
| LOG.warn( | ||
| "Skip registering Paimon session extensions because {} is not supported yet.", | ||
| PAIMON_PROVIDER); |
There was a problem hiding this comment.
Why don't we throw an exception here
…ark 4.0 support - SparkCatalogs on 3.5 caught only ClassNotFoundException when probing the Paimon catalog. Class.forName links the class, so a deployment without the Paimon runtime, which is the documented default, got NoClassDefFoundError while initializing the table, breaking Hive and JDBC sessions too. Reproduced on a Paimon-free classpath; now catches LinkageError as well. - Documented the Spark 4 exception contract in GravitinoIcebergCatalogSpark40: Spark 4 ships no NoSuchProcedureException and wraps non-SparkThrowable in FAILED_TO_LOAD_ROUTINE, which is why Iceberg's own BaseCatalog throws a plain RuntimeException there. - Fixed the runtime artifact links in spark-connector.md, which omitted the Scala suffix the artifacts are published with. - Moved the two partition delimiter constants above the instance field in HiveGravitinoOperationOperator, per the repository's member order.
…itinoSparkPlugin Each version module now owns GravitinoSparkPlugin, which builds a SparkBindings and passes it to the shared GravitinoDriverPlugin. The FQN is unchanged, so spark.plugins is the same for users. The dependency runs version module to shared code, ordinary Java with no import that resolves only after source-set composition. Three things follow. The authorization extension becomes a binding, so it moves out of the flavor directories into each version module and spark40 goes away entirely, leaving Paimon as spark35's only remaining user. A version module that forgets a catalog now fails a Preconditions check when the plugin is instantiated, rather than the first time that provider is used. And the dispatch is testable: TestGravitinoDriverPlugin supplies its own bindings and asserts what lands in spark.sql.catalog.*, independently of what a module ships. Replaces the per-version SparkCatalogs and TestSparkCatalogs from e321b66.
…he dispatch with tests A cr-fix pass over the review round that introduced SparkBindings found the builder accepted values that break at use time, and the catalog dispatch it feeds had no unit coverage at all. SparkBindings.Builder now rejects a blank class name and a kind bound twice, and the same for the authorization extension. A blank one there was the worst case: Spark's own parser filters blank entries out of spark.sql.extensions, so the authorization extension would be dropped and every session would run unauthorized, with no error and no log line. Both Class overloads narrowed to Class<? extends TableCatalog> and Class<? extends Function1<SparkSessionExtensions, Void>>, so "the compiler checks it" is now true. registerGravitinoCatalogs resolves the kind once instead of comparing raw provider literals, keeping the two opt-in gates in step with SparkCatalogKind rather than duplicating the mapping. The conf reads move into registerOptInExtensions so the keys are inside the tested seam. Tests: the provider-to-class dispatch, what registerCatalog writes under spark.sql.catalog.<name>, the four opt-in flag combinations over both catalogs and session extensions, merging into a user's own spark.sql.extensions, and the binding rejections. Each version module gains a test that builds its own GravitinoSparkPlugin, so a forgotten catalog fails a unit test rather than SparkSession startup; v3.5 also pins the Paimon catalog name, which is a string because the Scala 2.13 build compiles that package out. 3.5 at 136 cases under Scala 2.12 and green under 2.13, 4.0 at 132. No failures.
|
To @diqiu50 Done in b2a7cd1, with follow-up validation and tests in 65e24fa. All three things you named came out as you described.
The authorization extension became a binding, which let both flavor copies move into their own version module. A missing binding fails in the builder, and so does a duplicate or a blank one. The dispatch is testable, and tested. Paimon stays a string in |
Resolve the import block of TestGravitinoDriverPlugin: keep both sides' imports, drop GravitinoAuthorizationSparkSessionExtensions since this branch's tests bind org.example.* names instead.
|
Merge upstream into this branch and resolved the conflicts. |
diqiu50
left a comment
There was a problem hiding this comment.
Test coverage on shared logic is solid, but the new Spark 4.0-specific code (authorization, Iceberg REST backend, procedure loading) is largely untested.
| (TableChange.UpdateColumnDefaultValue) change; | ||
| return org.apache.gravitino.rel.TableChange.updateColumnDefaultValue( | ||
| updateColumnDefaultValue.fieldNames(), | ||
| Literals.stringLiteral(updateColumnDefaultValue.newDefaultValue())); |
There was a problem hiding this comment.
Is there any issue with using String for all of these fields?
| if ("lakehouse-paimon".equals(provider.toLowerCase(Locale.ROOT)) | ||
| && !enablePaimonSupport) { | ||
| if (SparkCatalogKind.LAKEHOUSE_PAIMON.equals(kind) && !enablePaimonSupport) { | ||
| return; |
There was a problem hiding this comment.
Duplicated check SparkCatalogKind.
| return true; | ||
| } catch (ClassNotFoundException | LinkageError e) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
We shouldn't swallow the exception.
| /** | ||
| * Registers Gravitino authorization checks with a Spark session. | ||
| * | ||
| * <p>This is the Spark 4 flavor. It matches the Spark 3.5 one under {@code src/main/spark35} except |
Resolve build.yml: upstream raised the build job cap to 120 minutes on its own in apache#12583, so drop the comment this branch added to justify the same bump. The branch no longer touches timeout-minutes.
… the 4.0 ITs Resolve the provider to kind mapping once on the catalog register path, log why the Paimon presence probe failed, fix two stale javadoc claims, and add the Spark 4.0 authorization and Iceberg REST backend IT subclasses.
|
@diqiu50 Done in 270a2b0, with main merged in. Three of the four inline comments were right and are fixed.
On coverage: the Iceberg REST backend was a real gap and now has a 4.0 subclass, along with the two authorization ITs. All three are picked up without a workflow change, since the Spark IT job selects by package. The other two you named are already covered on 4.0, which the diff does not make obvious. |
Resolve three things from apache#12709, which added Iceberg REST routing with a per-version IT: drop the 3.3 and 3.4 subclasses, whose modules this branch removed and which git tried to relocate into v4.0; add the 4.0 subclass with the metadata-column override its two siblings use; and take upstream's 120-minute cap on the Spark IT job.
|
THX, I also need Spark4.x version. I just cherry-pick this PR for compatible test. HOPE for merging soon. |
OK,Let's ignore the default value issue in this PR |
|
Thank you @diqiu50 and @jerryshao |
What changes were proposed in this pull request?
This revision replaces what this branch previously proposed. The earlier one added Spark 4.0 on top of the 3.3/3.4/3.5 matrix; review here asked for the 3.x matrix to shrink first and, per @diqiu50, for the initial submission to cover two versions so the approach and its maintenance cost can be judged from something concrete. The final matrix is Spark 3.5 and 4.0. Spark 4.1 exists locally as a follow-up and is not part of this PR.
Three commits, each self-contained:
1. Support Spark 3.5 only for the 3.x line. Spark 3.3 and 3.4 are past their maintenance windows; 3.5.x is on extended LTS through November 2027, so it is the only 3.x line that will see another release. With them gone, adaptations that existed only to bridge 3.3/3.4 collapse back into
spark-common: theTimestampNTZTypemapping andUpdateColumnDefaultValueconversion move intoSparkTypeConverterandSparkTableChangeConverter,loadTable(Identifier, Set<TableWritePrivilege>)moves intoBaseCatalog, and four converter subclasses plus the two version modules go away.SparkHiveTableandHiveGravitinoOperationOperatorswitch to the pluralPartitionsAlreadyExistException, becauseSupportsPartitionManagement.createPartitionchanged itsthrowsclause in Spark 3.4.2. Compile
spark-commonas a shared source set.spark-commonstops being a Gradle module. Each version module composes its own source set fromspark-common/src/main/java,spark-common/src/main/spark<NN>and its ownsrc/main/java. The test tree works the same way, replacing thetestArtifactsjar the version modules used to consume.Compiling the shared code once against one Spark version and running it on another is how a version-specific bug hides: the compiler only ever sees the API the pinned version exposes. Per-version compilation means each line's compiler checks the shared code against its own API, and the shared tests run on each line rather than only on the pinned one. Commit 3 shows this paying off immediately.
3. Add Spark 4.0 support. New
v4.0/sparkandv4.0/spark-runtime, publishinggravitino-spark-4.0_2.13andgravitino-spark-connector-runtime-4.0_2.13. Spark 4 is Scala 2.13 only and needs JDK 17, so both modules pin 2.13 rather than reading-PscalaVersion, and they opt out of the repository's JDK 8 target.Each version module owns its
GravitinoSparkPlugin, at the same fully-qualified name, sospark.pluginsis unchanged for users. It binds the classes its own Spark version needs and passes them to the shared driver plugin asSparkBindings, keyed by a sharedSparkCatalogKindenum. The bindings are compile-time class references, so a renamed or missing class fails the build rather than the session, and adding a Spark version no longer touches shared code. The authorization parser is one of those bindings, because Spark 4 added an abstractparseRoutineParamtoParserInterface, so each module carries its own copy.The one shared flavor directory left is
spark35, holding the Paimon package: Paimon publishes nopaimon-spark-4.xartifact at the version this repository pins, and nopaimon-spark-3.5_2.13either, so both the Spark 4 build and the Scala 2.13 build compile it out. The Paimon catalog is therefore the one binding named by string rather than by class reference, added only when the class is present, and the driver plugin skips the Paimon session extension when no Paimon catalog is bound rather than failingSparkSessionconstruction.Iceberg's
loadProceduremoves out of the sharedGravitinoIcebergCataloginto the per-version catalog subclasses. On 3.xProcedureCatalogis a class Iceberg ships andloadProcedurereturns Iceberg'sProcedure; on 4.x Spark ships its own and the return type isUnboundProcedure.implementsis declaration-level, so this one belongs in the version modules rather than in a flavor file.Two fixes to the shared code, both surfaced by compiling the shared tests against Spark 4 for the first time:
SparkTypeConverterandSparkPartitionUtilscheckedinstanceof StringTypebeforeVarcharTypeandCharType. Spark 4 made both extendStringType, so that branch swallowed them and silently dropped the length. Three shared tests caught it. Subtypes are now checked first, which is correct on every supported version.new Column(Literal.create(...)). Spark 4 rebuiltColumnonColumnNodeand dropped that constructor; it now usesfunctions.lit(), available on both.The 4.0 ITs pin Jersey, HK2 and jakarta.validation back to the javax flavor on the test runtime classpath: the embedded Gravitino server serves REST on Jetty 9 with Jersey 2, and Spark 4 brings the jakarta flavor transitively via
spark-hive. Spark's own web UI cannot coexist with that, so the ITs disable it. Both are test-only; the runtime jar bundles neither Jersey nor the server.Why are the changes needed?
Spark 4.0 is GA and the connector topped out at 3.5, leaving Spark 4 users with no supported path. The
VarcharTypebug above is the concrete argument for per-version compilation: it was reachable from any Spark 4 session and invisible to a build that only ever compiled the shared code against 3.x.Fix: #8771
Does this PR introduce any user-facing change?
Yes. Two removals, one addition, and one behavior change on the JDBC PostgreSQL catalog.
gravitino-spark-connector-runtime-3.3and-3.4are no longer published. Spark 3.3 and 3.4 users stay on an earlier Gravitino release.gravitino-spark-commonis no longer published. It was an implementation detail of this repository's own version modules, which now compile its sources directly; nothing outside the repository consumed it.gravitino-spark-4.0_2.13andgravitino-spark-connector-runtime-4.0_2.13are new. On Spark 4 the connector requires Scala 2.13 and JDK 17, and the Paimon catalog is unavailable. Glue resolves a catalog class on 4.0 but is verified on 3.5 only, because the patched Hive JARs it needs are published for Spark 3 alone.GravitinoPostgreSqlCatalogSpark35extends the shared JDBC catalog directly rather than a*Spark34subclass, so it never picked up the three version-specific overrides the other five catalogs had. Folding those into the shared classes gives it all three at once:timestamp_ntzcolumns are accepted instead of rejected;ALTER TABLE … ALTER COLUMN … SET DEFAULTworks instead of failing in the connector (the PostgreSQL backend already supported it); andloadTable(Identifier, Set<TableWritePrivilege>)now routes through the write path, so write operations on a PostgreSQL catalog are checked against write privileges where previously they were not. A user whose grants cover reads but not writes can start seeing denials. This aligns jdbc-postgresql with jdbc-mysql, which has had all three since [#10181] feat(spark-connector): Support TableWritePrivilege for Spark 3.5+ authorization #10194.How was this patch tested?
Unit tests cover the shared code on both lines, since the shared test tree compiles into each version module: type and property conversion, the provider-to-catalog dispatch, what the driver plugin writes under
spark.sql.catalog.<name>, the Iceberg and Paimon opt-in flags over both catalogs and session extensions, and the binding validation. Each version module also has a test that builds its ownGravitinoSparkPlugin, so a catalog a module forgot to bind fails there rather than atSparkSessionstartup, andv3.5pins the string-named Paimon catalog against the class it ships.Docker ITs run on both lines: Hive, Iceberg on the Hive backend, MySQL and PostgreSQL on 4.0, and the full 3.5 suite as a regression check. Both runtime shadow jars were built and inspected: the 4.0 jar carries its five
*Spark40catalogs and no Paimon classes, the 3.5 jar its own set plus Paimon on Scala 2.12.