環境
EMRは5.7。
aws-java-sdk-dynamodbのバージョンは下記。
libraryDependencies += "com.amazonaws" % "aws-java-sdk-dynamodb" % "1.11.170"
sbt assembly
で固めたFAT-JARをEMR上にデプロイ&実行するとエラーが発生。
ローカルでは上手く動くので原因がわからず少し困った。
事象
EMR5.7にて AmazonDynamoDBClientBuilder
を利用すると NoClassDefFoundError
。
AmazonDynamoDBClientBuilder
はパスを通している(FAT-JARに含まれている)ので、ClassNotFoundException
では無いだろうと思っていたが、原因が特定できず戸惑った。
17/08/14 10:17:39 INFO Client:
(略)
java.lang.NoClassDefFoundError: Could not initialize class com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder
at SparkApp$$anonfun$main$1$$anonfun$1.apply(SparkApp.scala:58)
at SparkApp$$anonfun$main$1$$anonfun$1.apply(SparkApp.scala:51)
at org.apache.spark.rdd.RDD$$anonfun$mapPartitions$1$$anonfun$apply$23.apply(RDD.scala:797)
at org.apache.spark.rdd.RDD$$anonfun$mapPartitions$1$$anonfun$apply$23.apply(RDD.scala:797)
at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:323)
at org.apache.spark.rdd.RDD.iterator(RDD.scala:287)
(略)
しばらく動かしたりログを探すと、IllegalAccessErrorがでていた。
どうやらこれが原因なようだ。
17/08/14 11:04:29 INFO Client:
(略)
java.lang.IllegalAccessError: tried to access class com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientConfigurationFactory from class com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder
at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder.<clinit>(AmazonDynamoDBClientBuilder.java:27)
at SparkApp$$anonfun$main$1$$anonfun$1.apply(SparkApp.scala:58)
at SparkApp$$anonfun$main$1$$anonfun$1.apply(SparkApp.scala:51)
at org.apache.spark.rdd.RDD$$anonfun$mapPartitions$1$$anonfun$apply$23.apply(RDD.scala:797)
at org.apache.spark.rdd.RDD$$anonfun$mapPartitions$1$$anonfun$apply$23.apply(RDD.scala:797)
at org.apache.spark.rdd.MapPartitionsRDD.compute(MapPartitionsRDD.scala:38)
at org.apache.spark.rdd.RDD.computeOrReadCheckpoint(RDD.scala:323)
at org.apache.spark.rdd.RDD.iterator(RDD.scala:287)
調査
調べるといくつか出てきた。
- https://stackoverflow.com/questions/43237791/illegalaccesserror-when-running-spark-job-in-emr
- https://stackoverflow.com/questions/45197850/cant-create-dynamodb-client-in-spark-executor
I was Finally able to solve it by using lower version of DynamoDB API.
The EMR 5.7 only supports 1.10.75.1. Below is the code that works fine for me.
どうやら、EMR5.7はaws-java-sdkのバージョンが 1.10.75.1
までしか対応していないとのこと。現在(※2017/08/15)は1.11系が出ているので、けっこう古い。というか本当なのか?
ということで、公式のドキュメントを調べると...
AWS SDK for Java が 1.10.75 にアップグレード
と書いてあり、1.10.75
までは対応していることは見つけたが、それ以降のバージョンについては述べていない。うーむ、信頼できるのか?
対応
ということで、半信半疑ながらSDKのバージョンをダウングレードした。
libraryDependencies += "com.amazonaws" % "aws-java-sdk-dynamodb" % "1.10.75.1"
そうすると、AmazonDynamoDBClientBuilder
が利用できないので、今ではdeplicatedになっているAmazonDynamoDBClient
を利用して書き換えすることに。
val client = AmazonDynamoDBClientBuilder.standard
.withRegion("ap-northeast-1")
.build
val dynamoDB = new DynamoDB(client)
こちら↑をすこし編集するのみで動いてくれた。
val client = new AmazonDynamoDBClient()
client.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1))
val dynamoDB = new DynamoDB(client)
実行結果
実行すると...
上手く動いてくれた。さすがStackOverFlow!
なぜ IllegalAccessError
が出てしまうかだが、EMR上でもaws-java-sdkがロードされていると思うので、かみ合わせが悪いのか?このあたりよく分かっていない。
まとめ
EMRでDynamoDBに書き込む時は、SDKのバージョンに気をつけよう。