Skip to content

Build: make JMH harness JDK 17-clean (module opens, configurable heap, failOnError)#17331

Open
vaquarkhan wants to merge 1 commit into
apache:mainfrom
vaquarkhan:benchmark-jdk17-harness
Open

Build: make JMH harness JDK 17-clean (module opens, configurable heap, failOnError)#17331
vaquarkhan wants to merge 1 commit into
apache:mainfrom
vaquarkhan:benchmark-jdk17-harness

Conversation

@vaquarkhan

Copy link
Copy Markdown

What

Update jmh.gradle so the benchmark suite runs on a clean JDK 17 checkout:

  • add the --add-opens set Spark 4.x needs (sun.util.calendar, nio, security, concurrent)
  • make heap configurable: -PjmhHeap (default preserved at 32g)
  • make failOnError configurable: -PjmhFailOnError (default preserved at true)

Why

On JDK 17 the read-path benchmarks throw IllegalAccessException (sun.util.calendar) at warm-up, and the full-scale PlanningBenchmark WithStats OOMs at 32g. Contributors currently cannot reproduce benchmarks without local edits. See #17330.

Testing

  • JDK 17: ./gradlew help, ./gradlew spotlessApply, ./gradlew :iceberg-core:jmhClasses succeed with the updated harness
  • Defaults unchanged for existing users: heap 32g, failOnError true unless -P flags are passed
  • JDK 21: --add-opens remain inert via -XX:+IgnoreUnrecognizedVMOptions

Closes #17330

Add the --add-opens set Spark 4.x needs on JDK 17 (sun.util.calendar and
others), make the JMH heap configurable via -PjmhHeap (default 32g), and make
failOnError configurable via -PjmhFailOnError (default true). Without the opens
the read-path benchmarks throw IllegalAccessException at warm-up; at 32g the
full-scale PlanningBenchmark WithStats OOMs. Defaults preserved.

Closes apache#17330
@github-actions github-actions Bot added the build label Jul 22, 2026
@vaquarkhan

Copy link
Copy Markdown
Author

I hit this issue while running the Spark benchmarks on JDK 17 (Corretto 17.0.19). Every read-path benchmark I
tried died at the first warm-up iteration because Spark reflects into sun.util.calendar and that package
is not opened by default on JDK 17:

# Warmup Iteration   1: <failure>
java.lang.IllegalAccessException: symbolic reference class is not accessible:
  class sun.util.calendar.ZoneInfo, from interface
  org.apache.spark.sql.catalyst.util.SparkDateTimeUtils (unnamed module)
    at org.apache.spark.sql.catalyst.util.SparkDateTimeUtils.toJavaDate(SparkDateTimeUtils.scala:304)
    at org.apache.iceberg.spark.source.IcebergSourceBenchmark.appendAsFile(IcebergSourceBenchmark.java:136)
    at org.apache.iceberg.spark.source.IcebergSourceDeleteBenchmark.setupBenchmark(IcebergSourceDeleteBenchmark.java:73)

Once I added --add-opens=java.base/sun.util.calendar=ALL-UNNAMED (what this PR does) the benchmarks got
past warm-up and finished normally. Here is one running to completion after the fix:

# Warmup Iteration   1: 2.387 s/op
Iteration   1: 2.047 s/op
Iteration   2: 2.058 s/op
Iteration   3: 2.035 s/op
Iteration   4: 2.015 s/op
Iteration   5: 2.051 s/op
  mean = 2.041 ±(99.9%) 0.065 s/op

I also ran into the 32g heap limit on the full-scale PlanningBenchmark ...WithStats at the default 1.5M
file scale. It only completed after I raised the heap well above 32g (I ran the full-scale case at -Xmx220g
on a larger box and it finished with no OOM), which is why I made the heap configurable via -PjmhHeap in
this change.

On JDK 17 I confirmed ./gradlew help, ./gradlew spotlessApply, and ./gradlew :iceberg-core:jmhClasses
all pass with the updated harness, and the defaults are unchanged if you do not pass any -P flags.

Stack trace from the failing run (framework frames trimmed, marked with ...; otherwise verbatim)
org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 0.0 failed 1 times, most recent failure: Lost task 0.0 in stage 0.0 (TID 0) (executor driver): java.lang.IllegalAccessException: symbolic reference class is not accessible: class sun.util.calendar.ZoneInfo, from interface org.apache.spark.sql.catalyst.util.SparkDateTimeUtils (unnamed module @50de0926)
	at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:955)
	at java.base/java.lang.invoke.MethodHandles$Lookup.checkSymbolicClass(MethodHandles.java:3686)
	at java.base/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3646)
	at java.base/java.lang.invoke.MethodHandles$Lookup.findVirtual(MethodHandles.java:2680)
	at org.apache.spark.sql.catalyst.util.SparkDateTimeUtils.org$apache$spark$sql$catalyst$util$SparkDateTimeUtils$$getOffsetsByWallHandle(SparkDateTimeUtils.scala:280)
	at org.apache.spark.sql.catalyst.util.DateTimeUtils$.org$apache$spark$sql$catalyst$util$SparkDateTimeUtils$$getOffsetsByWallHandle(DateTimeUtils.scala:41)
	at org.apache.spark.sql.catalyst.util.SparkDateTimeUtils.toJavaDate(SparkDateTimeUtils.scala:304)
	at org.apache.spark.sql.catalyst.util.SparkDateTimeUtils.toJavaDate$(SparkDateTimeUtils.scala:299)
	at org.apache.spark.sql.catalyst.util.DateTimeUtils$.toJavaDate(DateTimeUtils.scala:41)
	at org.apache.spark.sql.catalyst.util.DateTimeUtils.toJavaDate(DateTimeUtils.scala)
	at org.apache.spark.sql.catalyst.expressions.GeneratedClass$SpecificSafeProjection.apply(Unknown Source)
	at org.apache.spark.sql.execution.BufferedRowIterator.hasNext(BufferedRowIterator.java:43)
	at org.apache.spark.sql.connector.write.DataWriter.writeAll(DataWriter.java:107)
	at org.apache.spark.sql.execution.datasources.v2.DataWritingSparkTask$.run(WriteToDataSourceV2Exec.scala:631)
	... spark scheduler and executor frames ...
	at org.apache.spark.sql.classic.DataFrameWriter.save(DataFrameWriter.scala:115)
	at org.apache.iceberg.spark.source.IcebergSourceBenchmark.appendAsFile(IcebergSourceBenchmark.java:136)
	at org.apache.iceberg.spark.source.IcebergSourceDeleteBenchmark.writeData(IcebergSourceDeleteBenchmark.java:189)
	at org.apache.iceberg.spark.source.parquet.IcebergSourceParquetEqDeleteBenchmark.appendData(IcebergSourceParquetEqDeleteBenchmark.java:43)
	at org.apache.iceberg.spark.source.IcebergSourceDeleteBenchmark.setupBenchmark(IcebergSourceDeleteBenchmark.java:73)
	... jmh runner frames ...
Caused by: java.lang.IllegalAccessException: symbolic reference class is not accessible: class sun.util.calendar.ZoneInfo, from interface org.apache.spark.sql.catalyst.util.SparkDateTimeUtils (unnamed module @50de0926)
	at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:955)
	at java.base/java.lang.invoke.MethodHandles$Lookup.findVirtual(MethodHandles.java:2680)
	at org.apache.spark.sql.catalyst.util.SparkDateTimeUtils.toJavaDate(SparkDateTimeUtils.scala:304)
	... 3 more

The failing run's VM options did not include sun.util.calendar:

-Xmx100g --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/sun.security.action=ALL-UNNAMED

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Benchmarks: make jmh.gradle run cleanly on JDK 17 (module opens, configurable heap, failOnError)

1 participant