-
Notifications
You must be signed in to change notification settings - Fork 1k
[KYUUBI #6315] Spark 3.5: MaxScanStrategy supports DSv2 #5852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
fe620ca
b15652f
5f1c3c0
6061f42
661834c
dc128bc
cf893a0
73258c2
b307022
f87cb07
4d26ce1
3a07396
70c845b
c8399a0
acc3587
fb113d6
3c5b0c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,8 +23,10 @@ import org.apache.spark.sql.catalyst.SQLConfHelper | |
| import org.apache.spark.sql.catalyst.catalog.{CatalogTable, HiveTableRelation} | ||
| import org.apache.spark.sql.catalyst.planning.ScanOperation | ||
| import org.apache.spark.sql.catalyst.plans.logical.LogicalPlan | ||
| import org.apache.spark.sql.connector.read.SupportsReportPartitioning | ||
| import org.apache.spark.sql.execution.SparkPlan | ||
| import org.apache.spark.sql.execution.datasources.{CatalogFileIndex, HadoopFsRelation, InMemoryFileIndex, LogicalRelation} | ||
| import org.apache.spark.sql.execution.datasources.v2.DataSourceV2ScanRelation | ||
| import org.apache.spark.sql.types.StructType | ||
|
|
||
| import org.apache.kyuubi.sql.KyuubiSQLConf | ||
|
|
@@ -232,6 +234,54 @@ case class MaxScanStrategy(session: SparkSession) | |
| logicalRelation.catalogTable) | ||
| } | ||
| } | ||
| case ScanOperation( | ||
| _, | ||
| _, | ||
| _, | ||
| relation @ DataSourceV2ScanRelation(_, _, _, _, _)) => | ||
| val table = relation.relation.table | ||
| if (table.partitioning().nonEmpty && | ||
| relation.scan.isInstanceOf[SupportsReportPartitioning]) { | ||
| val partitionColumnNames = table.partitioning().map(_.describe()) | ||
| val stats = relation.computeStats() | ||
| lazy val scanFileSize = stats.sizeInBytes | ||
| lazy val scanPartitions = relation.scan.asInstanceOf[SupportsReportPartitioning] | ||
| .outputPartitioning() | ||
| .numPartitions() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is the task number of RDD/stage, instead of the table's partition number, does taskGroups in Iceberg means same thing?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's the input RDD partition number for iceberg datasource. Maybe the value of it is equal to table's partition number, but they're not the same thing. Seems it's a bit hard to get the number of scan table partitions. |
||
| if (maxScanPartitionsOpt.exists(_ < scanPartitions)) { | ||
| throw new MaxPartitionExceedException( | ||
| s""" | ||
| |Your SQL job scan a whole huge table without any partition filter, | ||
| |You should optimize your SQL logical according partition structure | ||
| |or shorten query scope such as p_date, detail as below: | ||
| |Table: ${table.name()} | ||
| |Partition Structure: ${partitionColumnNames.mkString(",")} | ||
| |""".stripMargin) | ||
| } | ||
| if (maxFileSizeOpt.exists(_ < scanFileSize)) { | ||
|
wForget marked this conversation as resolved.
|
||
| throw new MaxFileSizeExceedException( | ||
| s""" | ||
| |SQL job scan file size in bytes: $scanFileSize | ||
| |exceed restrict of table scan maxFileSize ${maxFileSizeOpt.get} | ||
| |You should optimize your SQL logical according partition structure | ||
| |or shorten query scope such as p_date, detail as below: | ||
| |Table: ${table.name()} | ||
| |Partition Structure: ${partitionColumnNames.mkString(",")} | ||
| |""".stripMargin) | ||
| } | ||
| } else { | ||
| val stats = relation.computeStats() | ||
| lazy val scanFileSize = stats.sizeInBytes | ||
| if (maxFileSizeOpt.exists(_ < scanFileSize)) { | ||
| throw new MaxFileSizeExceedException( | ||
| s""" | ||
| |SQL job scan file size in bytes: $scanFileSize | ||
| |exceed restrict of table scan maxFileSize ${maxFileSizeOpt.get} | ||
| |detail as below: | ||
| |Table: ${table.name()} | ||
| |""".stripMargin) | ||
| } | ||
| } | ||
| case _ => | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package org.apache.spark.sql | ||
|
|
||
| import java.util.OptionalLong | ||
|
|
||
| import org.apache.spark.sql.connector.{RangeInputPartition, SimpleBatchTable, SimpleScanBuilder, SimpleWritableDataSource} | ||
| import org.apache.spark.sql.connector.catalog.Table | ||
| import org.apache.spark.sql.connector.expressions.{Expressions, FieldReference, Transform} | ||
| import org.apache.spark.sql.connector.read.{InputPartition, ScanBuilder, Statistics, SupportsReportPartitioning, SupportsReportStatistics} | ||
| import org.apache.spark.sql.connector.read.partitioning.{KeyGroupedPartitioning, Partitioning} | ||
| import org.apache.spark.sql.util.CaseInsensitiveStringMap | ||
|
|
||
| class ReportStatisticsAndPartitionAwareDataSource extends SimpleWritableDataSource { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to add a new data source? Is it better to use iceberg datasource directly? @pan3793 WDYT?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer to use a dummy DS like Spark does. |
||
|
|
||
| class MyScanBuilder( | ||
| val partitionKeys: Seq[String]) extends SimpleScanBuilder | ||
| with SupportsReportStatistics with SupportsReportPartitioning { | ||
|
|
||
| override def estimateStatistics(): Statistics = { | ||
| new Statistics { | ||
| override def sizeInBytes(): OptionalLong = OptionalLong.of(80) | ||
|
|
||
| override def numRows(): OptionalLong = OptionalLong.of(10) | ||
|
|
||
| } | ||
| } | ||
|
|
||
| override def planInputPartitions(): Array[InputPartition] = { | ||
| Array(RangeInputPartition(0, 5), RangeInputPartition(5, 10)) | ||
| } | ||
|
|
||
| override def outputPartitioning(): Partitioning = { | ||
| new KeyGroupedPartitioning(partitionKeys.map(FieldReference(_)).toArray, 10) | ||
| } | ||
| } | ||
|
|
||
| override def getTable(options: CaseInsensitiveStringMap): Table = { | ||
| new SimpleBatchTable { | ||
| override def newScanBuilder(options: CaseInsensitiveStringMap): ScanBuilder = { | ||
| new MyScanBuilder(Seq("i")) | ||
| } | ||
|
|
||
| override def partitioning(): Array[Transform] = { | ||
| Array(Expressions.identity("i")) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package org.apache.spark.sql | ||
|
|
||
| import java.util.OptionalLong | ||
|
|
||
| import org.apache.spark.sql.connector._ | ||
| import org.apache.spark.sql.connector.catalog.Table | ||
| import org.apache.spark.sql.connector.read._ | ||
| import org.apache.spark.sql.util.CaseInsensitiveStringMap | ||
|
|
||
| class ReportStatisticsDataSource extends SimpleWritableDataSource { | ||
|
|
||
| class MyScanBuilder extends SimpleScanBuilder | ||
| with SupportsReportStatistics { | ||
|
|
||
| override def estimateStatistics(): Statistics = { | ||
| new Statistics { | ||
| override def sizeInBytes(): OptionalLong = OptionalLong.of(80) | ||
|
|
||
| override def numRows(): OptionalLong = OptionalLong.of(10) | ||
| } | ||
| } | ||
|
|
||
| override def planInputPartitions(): Array[InputPartition] = { | ||
| Array(RangeInputPartition(0, 5), RangeInputPartition(5, 10)) | ||
| } | ||
|
|
||
| } | ||
|
|
||
| override def getTable(options: CaseInsensitiveStringMap): Table = { | ||
| new SimpleBatchTable { | ||
| override def newScanBuilder(options: CaseInsensitiveStringMap): ScanBuilder = { | ||
| new MyScanBuilder | ||
| } | ||
| } | ||
| } | ||
| } |

Uh oh!
There was an error while loading. Please reload this page.