Sparkアプリケーション(Scala)でNoSuchMethodErrorになったら

経緯

BigQueryをDataFrameとして扱うため、こちらのライブラリに依存していました。 https://github.com/samelamin/spark-bigquery

build.sbt

  libraryDependencies ++= Seq(
    "com.github.samelamin" %% "spark-bigquery" % "0.2.5",
  ),

よしなに実装しspark-submitすると以下のようなエラーに遭遇。

Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V
    at com.google.cloud.hadoop.io.bigquery.BigQueryStrings.parseTableReference(BigQueryStrings.java:68)
    at com.google.cloud.hadoop.io.bigquery.BigQueryConfiguration.configureBigQueryInput(BigQueryConfiguration.java:246)
    at com.samelamin.spark.bigquery.BigQueryClient.selectQuery(BigQueryClient.scala:126)
    at com.samelamin.spark.bigquery.BigQuerySQLContext.bigQuerySelect(BigQuerySQLContext.scala:90)

原因

件のライブラリは google/guava というJava用のGoogleコアライブラリに依存しています。 guava はSpark自体にも含まれてるんですが、これが古いバージョンになってます。 spark 2.4.0なら SPARK_HOME/jars/guava-14.0.1.jar というjarファイルが見つかると思います。

sparkは「起動時」に SPARK_HOME/jars 以下をクラスパスに追加するため、アプリケーション側に依存関係を書いても上書かれてしまいます。 その結果 NoSuchMethodError が発生していたようです。

解決方法

sbt-assemblyの shade を利用する方法がgoogleのブログに紹介されていました。 https://cloud.google.com/blog/products/data-analytics/managing-java-dependencies-apache-spark-applications-cloud-dataproc

assemblyShadeRules in assembly := Seq(
  ShadeRule.rename("com.google.common.**" -> "repackaged.com.google.common.@1").inAll
),

こうすると依存する名前を変更することができ、依存の上書きを回避することができます。

また、名前が変わったので Merge Strategy に以下を追加しました。

case PathList("repackaged", "com", "google", xs@_*) => MergeStrategy.last

これで動くようになりました。

Shading はもっと細かいルールが決められるようなので、適宜設定しましょう。 https://github.com/sbt/sbt-assembly#shading

参考URL: