[LIVY-1041] Add Spark 4 support and make it the default build - #542
Open
roczei wants to merge 1 commit into
Open
[LIVY-1041] Add Spark 4 support and make it the default build#542roczei wants to merge 1 commit into
roczei wants to merge 1 commit into
Conversation
Contributor
Author
|
The integration test failed because Python 3.11.11 is missing from the test environment: This will be resolved once #540 is merged and the Docker image is updated. Related comment from @gyogal: #540 (comment) |
roczei
force-pushed
the
LIVY-1041
branch
3 times, most recently
from
August 24, 2026 12:10
1898890 to
52dcc5d
Compare
roczei
force-pushed
the
LIVY-1041
branch
4 times, most recently
from
August 24, 2026 20:30
b3a06ff to
a233e25
Compare
## What changes were proposed in this pull request? This PR adds support for building and running Apache Livy against Apache Spark 4.x (pinned to 4.1.2). Spark 4 is now the default build (`mvn package` with no extra profiles); Spark 3.x remains available via `-Pspark3 -Pscala-2.12`. Spark 4 introduced breaking changes that required widespread updates: JDK 17 minimum, Scala 2.13 only, Netty 4.2.x, a rewritten Scala REPL, and json4s 4.0.7. **Build / POM changes** - pom.xml: root properties default to Spark 4.1.2, Scala 2.13.17, Hadoop 3.4.1, and `java.version=17` so `mvn package` works without profile flags. The existing `spark3` and `scala-2.12` profiles override those defaults for legacy Spark 3 builds; dedicated `spark4` / `scala-2.13` profiles are no longer needed. Per Gyorgy's suggestion on [LIVY-1069](https://lists.apache.org/thread/xmb0hlqtmh0po2o75c025047x26lor9c), the latest supported Spark/Scala versions are now the default rather than requiring explicit `-P` flags. - JDK handling: maven-enforcer follows the active profile — JDK 17 for the default Spark 4 build, JDK 8 minimum when `-Pspark3` sets `java.version=1.8`. - maven-shade-plugin 3.5.0 -> 3.6.2 (bundles an ASM version that understands Java 22 bytecode, needed to shade Jackson 2.18.2's `META-INF/versions/22` multi-release classes). - repl/pom.xml: added `json4s-jackson-core` to the shade includes (json4s 4.0.7 split `JsonMethods` into a separate artifact; missing it caused a NoClassDefFoundError in PythonInterpreter at runtime). Also pulls `livy-test-lib` in as a test dependency so the repl tests can use the new shared `ScalaVersionAware` trait. - New Scala 2.13 module wrapper POMs: `core/scala-2.13`, `repl/scala-2.13`, `scala-api/scala-2.13`. - README.md: documented default Spark 4 build and Spark 3 profile usage; Spark 4 Python compatibility — supported Python versions for Spark 4.1 are 3.10 – 3.14. **Spark 4 / Scala 2.13 source-level fixes** - `repl/scala-2.13/SparkInterpreter.scala`: new implementation ported to the Spark 4 SparkILoop (`shell.ILoop`, `PrintWriter`, `createInterpreter(settings)`, operations via `sparkILoop.intp`, `ReplCompletion`). To make extra JARs supplied via `spark.jars.packages` visible to `import ...` we feed those JARs to the Scala compiler through the `-classpath` argument at Settings construction time, instead of via a post-init `IMain.addUrlsToClassPath` call. In Scala 2.13 the latter does not reliably register URLs with the compiler's `platform.classPath`, so `import org.codehaus.plexus.util._` fails with `object plexus is not a member of package org.codehaus`. A new `collectUserJarsClasspath()` helper walks the context classloader chain to Spark's `MutableURLClassLoader`, filters out `livy-*` and the wrong-version `scala-reflect` (same rules as the old `addUrls` path), and joins the resulting file paths with `File.pathSeparator`. If the chain has no `MutableURLClassLoader` we log a warning and skip the extra `-classpath` entry rather than passing an empty argument. - `AbstractSparkInterpreter.scala`: * `parseError` skips the leading caret line so `ename` lands on the "error: ..." message on both Scala 2.12 and 2.13 (2.13 prints the caret pointer before the message). Implemented with `lines.indexWhere(_.trim != "^") ... lines.patch(idx, Nil, 1)`. * New `isEffectivelyEmpty` helper strips block/line comments and returns success for comment-only inputs (Scala 2.12 accepted them silently; 2.13 rejects them as compile errors). - `repl/Session.scala`: SparkR `setJobGroup` match now accepts `"4"` alongside `"2" | "3"`. - `server/interactive/InteractiveSession.scala`: `datanucleusJars` and `mergeHiveSiteAndHiveDeps` now take an extra `scalaVersion` parameter, adds `case 3 | 4` to the major-version match and replaces the hard-coded `assembly/target/scala-2.12/jars` with `assembly/target/scala-$scalaVersion/jars`. - `server/batch/BatchSession.scala`: pass `--verbose` to spark-submit under `LivyConf.TEST_MODE` so tests can grep the resolved arguments out of the child's log. - `rsc/driver/SparkEntries.java`: new reflection-based `hiveClassesArePresent` helper that probes `org.apache.spark.sql.classic.SparkSession$` (Spark 4) first and falls back to `org.apache.spark.sql.SparkSession$` (Spark 3). - `utils/SparkProcessBuilder.scala`: new `verbose(v: Boolean)` mutator and an internal `_verbose` flag that appends `--verbose` to `spark-submit` when set (used by `BatchSession`). - `utils/LivySparkUtils.scala`: `(4, 0)` and `(4, 1)` -> `"2.13"` entries in `sparkScalaVersionMap`; `MAX_VERSION` bumped from `(3, 6)` to `(4, 2)`. - `repl/SparkRInterpreter.scala`: log a warning on Spark 4 that SparkR was deprecated in SPARK-49347 and may be removed in a future Spark release. - `scala-api/scalaapi/package.scala`: inline comment documenting that `Duration.isFinite` is a parameterless def in Scala 2.13 (took an empty parameter list in 2.12); dropping the parens compiles on both. - Thriftserver Scala/json4s 4.x updates: * `session/Get{Columns,Functions,Schemas,Tables}Job.java`: `scala.collection.JavaConversions.seqAsJavaList` (removed in Scala 2.13) replaced with `scala.collection.JavaConverters.seqAsJavaListConverter(...).asJava()`. * `types/DataTypeUtils.scala`: json4s 4.x `parse(input, useBigDecimal)` requires an `AsJsonInput[T]`; switch to the single-arg String overload `parse(sparkJson)`. * `LivyExecuteStatementOperation.scala`: explicit `res.toSeq` since Scala 2.13 no longer widens a mutable `Buffer` to `Seq` implicitly. * `ThriftServerSuites.scala`: `LIVY-571` assertion now also accepts the Spark 4 error string `[SCHEMA_NOT_FOUND] The schema `spark_catalog`.`invalid_database` cannot be found`. - `InteractiveSessionServlet.scala`: `logs.asJava` -> `logs.toSeq.asJava` so Scala 2.13 picks the `Seq[A]` -> `java.util.List` `asJava` overload instead of erroring on ambiguity. **Thriftserver libthrift pre-0.16 / 0.16 compatibility** The Livy Thrift binary CLI service was written against libthrift 0.9.3 (the version pinned in the root pom.xml). Spark 3 keeps a compatible pre-0.16 libthrift on the runtime classpath (e.g. 0.12.0 in Spark 3.3), but Spark 4 pulls in libthrift 0.16.0 transitively through spark-hive, and 0.16 removed four `TThreadPoolServer.Args` builder methods that Livy relied on: `requestTimeout`, `requestTimeoutUnit`, `beBackoffSlotLength` and `beBackoffSlotLengthUnit`. Compiling against 0.9.3 but running against 0.16.0 made the mini-cluster Thrift server abort during startup with a `NoSuchMethodError` on `TThreadPoolServer$Args.requestTimeout(int)`, which in turn hung JdbcIT on the SASL handshake. `ThriftBinaryCLIService` now invokes those four builders reflectively via a small `applyOptionalArg` helper: they run unchanged on Spark 3 classpaths and are silently skipped on Spark 4, where they no longer exist. The functional loss on Spark 4 is limited to the login-timeout / backoff knobs, which have no equivalent in the newer libthrift API. **Test fix: Scala 2.13 REPL output shape changes in InteractiveIT** The Scala 2.13 REPL prints results differently from Scala 2.12 in four ways that `InteractiveIT` was too strict about; the default Spark 4 build (Scala 2.13 only) trips all four. Fixed in place: - Value results: `val res0: Int = 2` (2.13) vs. `res0: Int = 2` (2.12). Six `verifyResult` regexes (across `basic interactive session`, `user jars are properly imported ...` and `recover interactive session`) now accept an optional `val ` prefix. - Compile errors: `error: not found: value abcde` (2.13) vs. `<console>:12: error: not found: value abcde` (2.12). The `<source>:<line>: ` location marker is now optional in the `verifyError` regex. - Class definitions: `class Item` (2.13) vs. `defined class Item` (2.12). The `case class Item(i: Int)` check in `user jars are properly imported ...` now accepts an optional `defined ` prefix. - Deprecation-warning header: Scala 2.13 prints a `warning: 1 deprecation ...` line above the value binding when the expression uses a deprecated API. `SparkContext.parallelize` is deprecated on Spark 4, so `val rdd = sc.parallelize(Array.fill(10){...})` prints the warning before the `val rdd: ...` line. The `verifyResult` regex now uses `(?s)(?:warning:.*\n)?(?:val )?rdd.*` to accept the warning prefix and the DOTALL semantics needed to span the newline. Both forms match on the default Scala 2.13 build and `-Pscala-2.12`. **Test fix: Spark 4 SQLContext / DataFrame runtime class in InteractiveIT** Spark 4 moved the runtime `SQLContext` and `DataFrame` implementations into `org.apache.spark.sql.classic`; only the compile-time aliases remain at `org.apache.spark.sql.SQLContext` / `org.apache.spark.sql.DataFrame`. The Scala 2.13 REPL surfaces the runtime class name, so Spark 4 prints `sql: org.apache.spark.sql.classic.SQLContext = ...` and `val df: org.apache.spark.sql.classic.DataFrame = ...`, where Spark 3 prints the alias form. Two `InteractiveIT` regexes now allow an optional `classic.` package qualifier so the same expectations match both the default Spark 4 build and `-Pspark3`: - `sql: org.apache.spark.sql.(?:classic\.)?SQLContext = ...` - `(?:val )?df: org.apache.spark.sql.(?:classic\.)?DataFrame` The old `Pattern.quote("df: org.apache.spark.sql.DataFrame")` literal was too strict for Spark 4 and caused the `basic interactive session` case to fail. **Cross-version test helpers: `ScalaVersionAware` trait** New `test-lib/.../ScalaVersionAware` trait exposes the small Scala-2.12 vs 2.13 REPL-output differences as reusable fragments: - `optionalValPrefix` (`"val "` on 2.13, `""` on 2.12) for exact-match expected strings. - `optionalValPrefixRegex` / `optionalDefinedPrefixRegex` / `optionalWarningPrefixRegex` regex fragments for the corresponding `verifyResult` regex assertions. Detection uses the runtime `scala.util.Properties.versionNumberString` so the same test class works regardless of which Scala the artifact was compiled against. Mixed into `BaseInterpreterSpec`, `BaseSessionSpec` and `InteractiveIT`. Downstream specs (`ScalaInterpreterSpec`, `SharedSessionSpec`, `SparkSessionSpec`, `InteractiveIT`) rewrote their expected-value strings against these fragments; some hard-`equal` assertions were relaxed to `include`/`fullyMatch regex` where the 2.13 REPL adds a deprecation banner ahead of the result or renumbers `resN` slots differently. **Server/unit tests: cross-Scala tolerance in `InteractiveSessionSpec`** The `should get scala version` case now decomposes the JSON result by hand and asserts `text/plain` equals either `res0: Int = 3\n` or `val res0: Int = 3\n`, plus separate `status` / `execution_count` assertions. Straight equality with a decomposed Map broke on the Spark 4 driver where the 2.13 REPL adds the `val ` prefix. **Unit tests: `LivySparkUtilsSuite`** Added `4.0.0` and `4.1.2` to both the supported-version list and the `defaultSparkScalaVersion` -> `"2.13"` expectations. **scala-api tests** - `ScalaClientTestUtils.scala`: `context.sc.parallelize(buffer, ...)` -> `context.sc.parallelize(buffer.toSeq, ...)` (2.13 no longer widens a mutable `ArrayBuffer` to `Seq` implicitly for the `parallelize` signature). - `ScalaJobHandleTest.scala`: `Duration.Undefined` -> `Duration.Inf` (2.13's `Await.ready` rejects `Undefined` with "Cannot wait for Undefined duration of time"); assertion switched from `verify(mockJobHandle, times(1)).get()` to `verify(mockJobHandle, atLeastOnce()).isDone`, because 2.13's `Await.ready` short-circuits when `isCompleted` is already true and does not call the underlying `ready(atMost)` -- so `.get()` is not observed on 2.13. `isDone` is exercised by both versions. **Repl tests: `SparkInterpreterSpec` move** `SparkInterpreterSpec.scala` moved from `repl/scala-2.12/src/test/...` into the shared `repl/src/test/scala/...` tree (it was byte-identical between scala-2.12 and scala-2.13). A new `should skip leading caret lines in Scala 2.13 error format.` test case was added for the caret- first `parseError` branch, so the shared spec runs three cases under both the default Scala 2.13 build and `-Pscala-2.12`. **CI matrix** - `.github/workflows/unit-tests.yaml` and `integration-tests.yaml` converted the flat `maven_profile` x `jdk_path` matrix to an explicit `include:` list. The default spark4 build (no extra Maven profiles) runs on JDK 17; Spark 3 matrix entries use `-Pscala-2.12 -Pspark3`. ## How was this patch tested? - Unit tests: `mvn verify -Pthriftserver` passes all 19 modules on JDK 17 (macOS aarch64) with the default Spark 4 build. Verified modules include livy-rsc (40 tests), livy-repl_2.13 (87 tests), livy-server (205 tests), livy-client-http (17 tests), livy-scala-api_2.13 (17 tests), and the thriftserver integration suite (27 tests: HttpThriftServerSuite + BinaryThriftServerSuite). The `SparkInterpreterSpec` move added one new caret-first `parseError` test case; the shared spec now has 3 cases and runs identically under both the default Scala 2.13 build and `-Pscala-2.12`. - Integration tests (`mvn integration-test -Pspark3 -Pscala-2.12 -pl :livy-integration-test`) pass. - GitHub Actions CI runs both spark3 and spark4 matrix entries for unit and integration test workflows. ## Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.7)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes were proposed in this pull request?
This PR adds support for building and running Apache Livy against Apache Spark 4.x (pinned to 4.1.2). Spark 4 is now the default build (
mvn packagewith no extra profiles); Spark 3.x remains available via-Pspark3 -Pscala-2.12.Spark 4 introduced breaking changes that required widespread updates: JDK 17 minimum, Scala 2.13 only, Netty 4.2.x, a rewritten Scala REPL, and json4s 4.0.7.
Build / POM changes
java.version=17somvn packageworks without profile flags. The existingspark3andscala-2.12profiles override those defaults for legacy Spark 3 builds; dedicatedspark4/scala-2.13profiles are no longer needed. Per Gyorgy's suggestion on LIVY-1069, the latest supported Spark/Scala versions are now the default rather than requiring explicit-Pflags.-Pspark3setsjava.version=1.8.META-INF/versions/22multi-release classes).json4s-jackson-coreto the shade includes (json4s 4.0.7 splitJsonMethodsinto a separate artifact; missing it caused a NoClassDefFoundError in PythonInterpreter at runtime). Also pullslivy-test-libin as a test dependency so the repl tests can use the new sharedScalaVersionAwaretrait.core/scala-2.13,repl/scala-2.13,scala-api/scala-2.13.Spark 4 / Scala 2.13 source-level fixes
repl/scala-2.13/SparkInterpreter.scala: new implementation ported to the Spark 4 SparkILoop (shell.ILoop,PrintWriter,createInterpreter(settings), operations viasparkILoop.intp,ReplCompletion). To make extra JARs supplied viaspark.jars.packagesvisible toimport ...we feed those JARs to the Scala compiler through the-classpathargument at Settings construction time, instead of via a post-initIMain.addUrlsToClassPathcall. In Scala 2.13 the latter does not reliably register URLs with the compiler'splatform.classPath, soimport org.codehaus.plexus.util._fails withobject plexus is not a member of package org.codehaus. A newcollectUserJarsClasspath()helper walks the context classloader chain to Spark'sMutableURLClassLoader, filters outlivy-*and the wrong-versionscala-reflect(same rules as the oldaddUrlspath), and joins the resulting file paths withFile.pathSeparator. If the chain has noMutableURLClassLoaderwe log a warning and skip the extra-classpathentry rather than passing an empty argument.AbstractSparkInterpreter.scala:parseErrorskips the leading caret line soenamelands on the "error: ..." message on both Scala 2.12 and 2.13 (2.13 prints the caret pointer before the message). Implemented withlines.indexWhere(_.trim != "^") ... lines.patch(idx, Nil, 1).isEffectivelyEmptyhelper strips block/line comments and returns success for comment-only inputs (Scala 2.12 accepted them silently; 2.13 rejects them as compile errors).repl/Session.scala: SparkRsetJobGroupmatch now accepts"4"alongside"2" | "3".server/interactive/InteractiveSession.scala:datanucleusJarsandmergeHiveSiteAndHiveDepsnow take an extrascalaVersionparameter, addscase 3 | 4to the major-version match and replaces the hard-codedassembly/target/scala-2.12/jarswithassembly/target/scala-$scalaVersion/jars.server/batch/BatchSession.scala: pass--verboseto spark-submit underLivyConf.TEST_MODEso tests can grep the resolved arguments out of the child's log.rsc/driver/SparkEntries.java: new reflection-basedhiveClassesArePresenthelper that probesorg.apache.spark.sql.classic.SparkSession$(Spark 4) first and falls back toorg.apache.spark.sql.SparkSession$(Spark 3).utils/SparkProcessBuilder.scala: newverbose(v: Boolean)mutator and an internal_verboseflag that appends--verbosetospark-submitwhen set (used byBatchSession).utils/LivySparkUtils.scala:(4, 0)and(4, 1)->"2.13"entries insparkScalaVersionMap;MAX_VERSIONbumped from(3, 6)to(4, 2).repl/SparkRInterpreter.scala: log a warning on Spark 4 that SparkR was deprecated in SPARK-49347 and may be removed in a future Spark release.scala-api/scalaapi/package.scala: inline comment documenting thatDuration.isFiniteis a parameterless def in Scala 2.13 (took an empty parameter list in 2.12); dropping the parens compiles on both.Thriftserver Scala/json4s 4.x updates:
session/Get{Columns,Functions,Schemas,Tables}Job.java:scala.collection.JavaConversions.seqAsJavaList(removed in Scala 2.13) replaced withscala.collection.JavaConverters.seqAsJavaListConverter(...).asJava().types/DataTypeUtils.scala: json4s 4.xparse(input, useBigDecimal)requires anAsJsonInput[T]; switch to the single-arg String overloadparse(sparkJson).LivyExecuteStatementOperation.scala: explicitres.toSeqsince Scala 2.13 no longer widens a mutableBuffertoSeqimplicitly.ThriftServerSuites.scala:LIVY-571assertion now also accepts the Spark 4 error string[SCHEMA_NOT_FOUND] The schemaspark_catalog.invalid_databasecannot be found.InteractiveSessionServlet.scala:logs.asJava->logs.toSeq.asJavaso Scala 2.13 picks theSeq[A]->java.util.ListasJavaoverload instead of erroring on ambiguity.Thriftserver libthrift pre-0.16 / 0.16 compatibility The Livy Thrift binary CLI service was written against libthrift 0.9.3 (the version pinned in the root pom.xml). Spark 3 keeps a compatible pre-0.16 libthrift on the runtime classpath (e.g. 0.12.0 in Spark 3.3), but Spark 4 pulls in libthrift 0.16.0 transitively through spark-hive, and 0.16 removed four
TThreadPoolServer.Argsbuilder methods that Livy relied on:requestTimeout,requestTimeoutUnit,beBackoffSlotLengthandbeBackoffSlotLengthUnit. Compiling against 0.9.3 but running against 0.16.0 made the mini-cluster Thrift server abort during startup with aNoSuchMethodErroronTThreadPoolServer$Args.requestTimeout(int), which in turn hung JdbcIT on the SASL handshake.ThriftBinaryCLIServicenow invokes those four builders reflectively via a smallapplyOptionalArghelper: they run unchanged on Spark 3 classpaths and are silently skipped on Spark 4, where they no longer exist. The functional loss on Spark 4 is limited to the login-timeout / backoff knobs, which have no equivalent in the newer libthrift API.Test fix: Scala 2.13 REPL output shape changes in InteractiveIT The Scala 2.13 REPL prints results differently from Scala 2.12 in four ways that
InteractiveITwas too strict about; the default Spark 4 build (Scala 2.13 only) trips all four. Fixed in place:val res0: Int = 2(2.13) vs.res0: Int = 2(2.12). SixverifyResultregexes (acrossbasic interactive session,user jars are properly imported ...andrecover interactive session) now accept an optionalvalprefix.error: not found: value abcde(2.13) vs.<console>:12: error: not found: value abcde(2.12). The<source>:<line>:location marker is now optional in theverifyErrorregex.class Item(2.13) vs.defined class Item(2.12). Thecase class Item(i: Int)check inuser jars are properly imported ...now accepts an optionaldefinedprefix.warning: 1 deprecation ...line above the value binding when the expression uses a deprecated API.SparkContext.parallelizeis deprecated on Spark 4, soval rdd = sc.parallelize(Array.fill(10){...})prints the warning before theval rdd: ...line. TheverifyResultregex now uses(?s)(?:warning:.*\n)?(?:val )?rdd.*to accept the warning prefix and the DOTALL semantics needed to span the newline. Both forms match on the default Scala 2.13 build and-Pscala-2.12.Test fix: Spark 4 SQLContext / DataFrame runtime class in InteractiveIT Spark 4 moved the runtime
SQLContextandDataFrameimplementations intoorg.apache.spark.sql.classic; only the compile-time aliases remain atorg.apache.spark.sql.SQLContext/org.apache.spark.sql.DataFrame. The Scala 2.13 REPL surfaces the runtime class name, so Spark 4 printssql: org.apache.spark.sql.classic.SQLContext = ...andval df: org.apache.spark.sql.classic.DataFrame = ..., where Spark 3 prints the alias form. TwoInteractiveITregexes now allow an optionalclassic.package qualifier so the same expectations match both the default Spark 4 build and-Pspark3:sql: org.apache.spark.sql.(?:classic\.)?SQLContext = ...(?:val )?df: org.apache.spark.sql.(?:classic\.)?DataFrameThe oldPattern.quote("df: org.apache.spark.sql.DataFrame")literal was too strict for Spark 4 and caused thebasic interactive sessioncase to fail.Cross-version test helpers:
ScalaVersionAwaretrait Newtest-lib/.../ScalaVersionAwaretrait exposes the small Scala-2.12 vs 2.13 REPL-output differences as reusable fragments:optionalValPrefix("val "on 2.13,""on 2.12) for exact-match expected strings.optionalValPrefixRegex/optionalDefinedPrefixRegex/optionalWarningPrefixRegexregex fragments for the correspondingverifyResultregex assertions. Detection uses the runtimescala.util.Properties.versionNumberStringso the same test class works regardless of which Scala the artifact was compiled against. Mixed intoBaseInterpreterSpec,BaseSessionSpecandInteractiveIT. Downstream specs (ScalaInterpreterSpec,SharedSessionSpec,SparkSessionSpec,InteractiveIT) rewrote their expected-value strings against these fragments; some hard-equalassertions were relaxed toinclude/fullyMatch regexwhere the 2.13 REPL adds a deprecation banner ahead of the result or renumbersresNslots differently.Server/unit tests: cross-Scala tolerance in
InteractiveSessionSpecTheshould get scala versioncase now decomposes the JSON result by hand and assertstext/plainequals eitherres0: Int = 3\norval res0: Int = 3\n, plus separatestatus/execution_countassertions. Straight equality with a decomposed Map broke on the Spark 4 driver where the 2.13 REPL adds thevalprefix.Unit tests:
LivySparkUtilsSuiteAdded
4.0.0and4.1.2to both the supported-version list and thedefaultSparkScalaVersion->"2.13"expectations.scala-api tests
ScalaClientTestUtils.scala:context.sc.parallelize(buffer, ...)->context.sc.parallelize(buffer.toSeq, ...)(2.13 no longer widens a mutableArrayBuffertoSeqimplicitly for theparallelizesignature).ScalaJobHandleTest.scala:Duration.Undefined->Duration.Inf(2.13'sAwait.readyrejectsUndefinedwith "Cannot wait for Undefined duration of time"); assertion switched fromverify(mockJobHandle, times(1)).get()toverify(mockJobHandle, atLeastOnce()).isDone, because 2.13'sAwait.readyshort-circuits whenisCompletedis already true and does not call the underlyingready(atMost)-- so.get()is not observed on 2.13.isDoneis exercised by both versions.Repl tests:
SparkInterpreterSpecmoveSparkInterpreterSpec.scalamoved fromrepl/scala-2.12/src/test/...into the sharedrepl/src/test/scala/...tree (it was byte-identical between scala-2.12 and scala-2.13). A newshould skip leading caret lines in Scala 2.13 error format.test case was added for the caret- firstparseErrorbranch, so the shared spec runs three cases under both the default Scala 2.13 build and-Pscala-2.12.CI matrix
.github/workflows/unit-tests.yamlandintegration-tests.yamlconverted the flatmaven_profilexjdk_pathmatrix to an explicitinclude:list. The default spark4 build (no extra Maven profiles) runs on JDK 17; Spark 3 matrix entries use-Pscala-2.12 -Pspark3.How was this patch tested?
mvn verify -Pthriftserverpasses all 19 modules on JDK 17 (macOS aarch64) with the default Spark 4 build. Verified modules include livy-rsc (40 tests), livy-repl_2.13 (87 tests), livy-server (205 tests), livy-client-http (17 tests), livy-scala-api_2.13 (17 tests), and the thriftserver integration suite (27 tests: HttpThriftServerSuite + BinaryThriftServerSuite). TheSparkInterpreterSpecmove added one new caret-firstparseErrortest case; the shared spec now has 3 cases and runs identically under both the default Scala 2.13 build and-Pscala-2.12.mvn integration-test -Pspark3 -Pscala-2.12 -pl :livy-integration-test) pass.Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 4.7)