Spark saveAsTable 位于 s3 存储桶的根本原因 NullPointerException
Spark saveAsTable with location at s3 bucket's root cause NullPointerException
我正在使用 Spark 3.0.1,我的分区 table 存储在 s3 中。请在此处找到问题的描述。
创建Table
Create table root_table_test_spark_3_0_1 (
id string,
name string
)
USING PARQUET
PARTITIONED BY (id)
LOCATION 's3a://MY_BUCKET_NAME/'
导致第二个 NullPointerException 的代码 运行
Seq(MinimalObject("id_1", "name_1"), MinimalObject("id_2", "name_2"))
.toDS()
.write
.partitionBy("id")
.mode(SaveMode.Append)
.saveAsTable("root_table_test_spark_3_0_1")
当 Hive Metastore 为空时,一切正常,但是当 spark 尝试在 InsertIntoHadoopFsRelationCommand
阶段执行 getCustomPartitionLocations
时,问题就出现了。 (以第二个运行为例)
实际上它调用了以下方法:from (org.apache.hadoop.fs.Path
)
/** Adds a suffix to the final name in the path.*/
public Path suffix(String suffix) {
return new Path(getParent(), getName()+suffix);
}
但是当我们处于根目录时,getParent()
将 return 为 null,从而导致 NullPointerException。我目前考虑的唯一选择是覆盖此方法来执行类似的操作:
/** Adds a suffix to the final name in the path.*/
public Path suffix(String suffix) {
return (isRoot()) ? new Path(uri.getScheme(), uri.getAuthority(), suffix) : new Path(getParent(), getName()+suffix);
}
有人在 spark hive table 的 LOCATION
处于根级别时遇到问题吗?任何解决方法?有没有打开任何已知问题?
我的运行时不允许我覆盖路径 class 并修复 suffix
方法,我无法从存储桶的根移动我的数据,因为它已经存在 2 年了。
出现此问题是因为我正在从 Spark 2.1.0 迁移到 Spark 3.0.1,并且检查自定义分区的行为出现在 Spark 2.2.0 (https://github.com/apache/spark/pull/16460)
整个上下文有助于理解问题,但基本上您可以轻松重现它
val path: Path = new Path("s3a://MY_BUCKET_NAME/")
println(path.suffix("/id=id"))
仅供参考。 hadoop-common 版本是 2.7.4,请在这里找到完整的堆栈跟踪
NullPointerException
at org.apache.hadoop.fs.Path.<init>(Path.java:104)
at org.apache.hadoop.fs.Path.<init>(Path.java:93)
at org.apache.hadoop.fs.Path.suffix(Path.java:361)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.$anonfun$getCustomPartitionLocations(InsertIntoHadoopFsRelationCommand.scala:262)
at scala.collection.TraversableLike.$anonfun$flatMap(TraversableLike.scala:245)
at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:62)
at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:55)
at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:49)
at scala.collection.TraversableLike.flatMap(TraversableLike.scala:245)
at scala.collection.TraversableLike.flatMap$(TraversableLike.scala:242)
at scala.collection.AbstractTraversable.flatMap(Traversable.scala:108)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.getCustomPartitionLocations(InsertIntoHadoopFsRelationCommand.scala:260)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.run(InsertIntoHadoopFsRelationCommand.scala:107)
at org.apache.spark.sql.execution.datasources.DataSource.writeAndRead(DataSource.scala:575)
at org.apache.spark.sql.execution.command.CreateDataSourceTableAsSelectCommand.saveDataIntoTable(createDataSourceTables.scala:218)
at org.apache.spark.sql.execution.command.CreateDataSourceTableAsSelectCommand.run(createDataSourceTables.scala:166)
谢谢
看起来像是 spark 代码调用 Path.suffix("something)
的情况,因为根路径没有父路径,所以触发了 NPE
长期修复
- 在 issues.apache.org 上针对 HADOOP 提交 JIRA;提供带有修复后缀()测试的补丁,以便在根路径上调用时正确降级。最适合所有人
- 不要将根路径用作 table 的目标。
- 这两个都做
选项 #2 应该避免关于 table 是如何 created/committed 等的其他意外......一些代码可能会失败,因为试图删除路径的根(这里是 s3a: //some-bucket") 不会删除根,对吗?
换句话说:根目录到处都有“奇怪”的语义;大多数时候你不会在本地 FS 上注意到这一点,因为你从不尝试使用 / 作为工作目的地,感到惊讶的是 rm -rf / 与 rm -rf /subdir 等不同。Spark,Hive 等从来没有写成使用 / 作为工作的目的地,所以你会看到失败。
我正在使用 Spark 3.0.1,我的分区 table 存储在 s3 中。请在此处找到问题的描述。
创建Table
Create table root_table_test_spark_3_0_1 (
id string,
name string
)
USING PARQUET
PARTITIONED BY (id)
LOCATION 's3a://MY_BUCKET_NAME/'
导致第二个 NullPointerException 的代码 运行
Seq(MinimalObject("id_1", "name_1"), MinimalObject("id_2", "name_2"))
.toDS()
.write
.partitionBy("id")
.mode(SaveMode.Append)
.saveAsTable("root_table_test_spark_3_0_1")
当 Hive Metastore 为空时,一切正常,但是当 spark 尝试在 InsertIntoHadoopFsRelationCommand
阶段执行 getCustomPartitionLocations
时,问题就出现了。 (以第二个运行为例)
实际上它调用了以下方法:from (org.apache.hadoop.fs.Path
)
/** Adds a suffix to the final name in the path.*/
public Path suffix(String suffix) {
return new Path(getParent(), getName()+suffix);
}
但是当我们处于根目录时,getParent()
将 return 为 null,从而导致 NullPointerException。我目前考虑的唯一选择是覆盖此方法来执行类似的操作:
/** Adds a suffix to the final name in the path.*/
public Path suffix(String suffix) {
return (isRoot()) ? new Path(uri.getScheme(), uri.getAuthority(), suffix) : new Path(getParent(), getName()+suffix);
}
有人在 spark hive table 的 LOCATION
处于根级别时遇到问题吗?任何解决方法?有没有打开任何已知问题?
我的运行时不允许我覆盖路径 class 并修复 suffix
方法,我无法从存储桶的根移动我的数据,因为它已经存在 2 年了。
出现此问题是因为我正在从 Spark 2.1.0 迁移到 Spark 3.0.1,并且检查自定义分区的行为出现在 Spark 2.2.0 (https://github.com/apache/spark/pull/16460)
整个上下文有助于理解问题,但基本上您可以轻松重现它
val path: Path = new Path("s3a://MY_BUCKET_NAME/")
println(path.suffix("/id=id"))
仅供参考。 hadoop-common 版本是 2.7.4,请在这里找到完整的堆栈跟踪
NullPointerException
at org.apache.hadoop.fs.Path.<init>(Path.java:104)
at org.apache.hadoop.fs.Path.<init>(Path.java:93)
at org.apache.hadoop.fs.Path.suffix(Path.java:361)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.$anonfun$getCustomPartitionLocations(InsertIntoHadoopFsRelationCommand.scala:262)
at scala.collection.TraversableLike.$anonfun$flatMap(TraversableLike.scala:245)
at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:62)
at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:55)
at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:49)
at scala.collection.TraversableLike.flatMap(TraversableLike.scala:245)
at scala.collection.TraversableLike.flatMap$(TraversableLike.scala:242)
at scala.collection.AbstractTraversable.flatMap(Traversable.scala:108)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.getCustomPartitionLocations(InsertIntoHadoopFsRelationCommand.scala:260)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.run(InsertIntoHadoopFsRelationCommand.scala:107)
at org.apache.spark.sql.execution.datasources.DataSource.writeAndRead(DataSource.scala:575)
at org.apache.spark.sql.execution.command.CreateDataSourceTableAsSelectCommand.saveDataIntoTable(createDataSourceTables.scala:218)
at org.apache.spark.sql.execution.command.CreateDataSourceTableAsSelectCommand.run(createDataSourceTables.scala:166)
谢谢
看起来像是 spark 代码调用 Path.suffix("something)
的情况,因为根路径没有父路径,所以触发了 NPE
长期修复
- 在 issues.apache.org 上针对 HADOOP 提交 JIRA;提供带有修复后缀()测试的补丁,以便在根路径上调用时正确降级。最适合所有人
- 不要将根路径用作 table 的目标。
- 这两个都做
选项 #2 应该避免关于 table 是如何 created/committed 等的其他意外......一些代码可能会失败,因为试图删除路径的根(这里是 s3a: //some-bucket") 不会删除根,对吗?
换句话说:根目录到处都有“奇怪”的语义;大多数时候你不会在本地 FS 上注意到这一点,因为你从不尝试使用 / 作为工作目的地,感到惊讶的是 rm -rf / 与 rm -rf /subdir 等不同。Spark,Hive 等从来没有写成使用 / 作为工作的目的地,所以你会看到失败。