Implementing DataSource V2 Read Path - #162
Conversation
There was a problem hiding this comment.
Pull request overview
Implements the Spark 4 DataSource V2 read path for HBase, including filter pushdown, partition planning, and row conversion.
Changes:
- Adds the V2 provider, table, scan, batch, partition, and reader pipeline.
- Ports row-key range and predicate handling.
- Adds unit and mini-cluster integration tests.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
ScanRange.scala |
Adds row-key range operations. |
HBaseTableProvider.scala |
Adds the V2 provider entry point. |
HBaseTable.scala |
Declares table schema and read capability. |
HBaseScanBuilder.scala |
Negotiates filters and projections. |
HBaseScan.scala |
Builds the logical HBase scan. |
HBaseBatch.scala |
Plans region-based input partitions. |
HBaseInputPartition.scala |
Defines serializable partitions. |
HBasePartitionReaderFactory.scala |
Creates partition readers. |
HBasePartitionReader.scala |
Executes scans and creates rows. |
ScanRangeSuite.scala |
Tests range operations. |
HBaseScanBuilderSuite.scala |
Tests filter and column pushdown. |
HBaseTableCatalogSuite.scala |
Tests catalog parsing. |
HBaseTableProviderSuite.scala |
Tests end-to-end reads. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
taklwu
left a comment
There was a problem hiding this comment.
may you fix the commit title with JIRA prefix HBASE-30189 ?
| .orElse(intersectedPoints.lastOption.map(Utils.incrementByteArray)) | ||
| .orElse(region.end) | ||
| .orNull | ||
| Some(HBaseInputPartition(region.index, startRow, stopRow): InputPartition) |
There was a problem hiding this comment.
seems like a behavior difference from V1 that HBaseInputPartition that uses HBasePartitionReader.scala are scanning instead of Get , do you think this is good ? if not , please try to align what Spark3 does with Get.
There was a problem hiding this comment.
We should use Get too. Changing this in the next commit.
| extends Batch | ||
| with Logging { | ||
|
|
||
| override def planInputPartitions(): Array[InputPartition] = { |
There was a problem hiding this comment.
we may need to support getPreferredLocations like V1 does , you can have it in the future PR.
There was a problem hiding this comment.
This method is defined in the RDD class, which is the model used in V1. V2 uses the Batch model, so there's no RDD subclassing.
I'm actually targeting this PR to the HBASE-30189 branch, which already has two previous commits related to this HBASE-30189. The idea was to split HBASE-30189 into several PRs for ease of review. After all PRs are in HBASE-30189, I'm going to merge it into master as a single commit for HBASE-30189. |
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated 8 comments.
|
(!) A patch to the testing environment has been detected. |
|
(!) A patch to the testing environment has been detected. |
d2bc359 to
31be97a
Compare
…job (apache#164) Signed-off-by: Tak Lon (Stephen) Wu <taklwu@apache.org> Signed-off-by: Dávid Paksy <paksyd@apache.org> Reviewed-by: Kevin Geiszler <kevin.j.geiszler@gmail.com>
Co-authored-by: Claude Code (claude-opus-4-6) <no-reply@anthropic.com> Signed-off-by: Peter Somogyi <psomogyi@apache.org>
…emas (apache#160) Co-authored-by: Claude Code (claude-opus-4-6) <no-reply@anthropic.com> Signed-off-by: Peter Somogyi <psomogyi@apache.org>
31be97a to
2552079
Compare
|
(!) A patch to the testing environment has been detected. |
There was a problem hiding this comment.
🟡 Changes recommended
Composite keys, unions, null handling, and configuration propagation still contain correctness or scalability defects.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (1)
spark4/hbase-spark4/src/main/scala/org/apache/hadoop/hbase/spark/datasources/HBasePartitionReader.scala:198
- The same missing-qualifier problem affects point reads: a
Getrestricted to theIsNullqualifier returns an emptyResultwhen that qualifier is absent, and line 202 drops it before the predicate can accept it. For pushed expressions that can match a missing cell, do not restrict theGetto only those qualifiers (or otherwise fetch an existence column).
scanFields.foreach { f =>
g.addColumn(f.cfBytes, f.colBytes)
}
- Files reviewed: 17/17 changed files
- Comments generated: 8
- Review effort level: Balanced
| val conf = HBaseConfiguration.create() | ||
| val configResources = properties.get(HBaseSparkConf.HBASE_CONFIG_LOCATION) | ||
| configResources.foreach(_.split(",").foreach(r => conf.addResource(new Path(r)))) |
| scanFields.foreach { f => | ||
| scan.addColumn(f.cfBytes, f.colBytes) | ||
| } |
| case EqualTo(attr, _) => catalog.sMap.map.contains(attr) | ||
| case LessThan(attr, _) => catalog.sMap.map.contains(attr) | ||
| case GreaterThan(attr, _) => catalog.sMap.map.contains(attr) | ||
| case LessThanOrEqual(attr, _) => catalog.sMap.map.contains(attr) | ||
| case GreaterThanOrEqual(attr, _) => catalog.sMap.map.contains(attr) | ||
| case StringStartsWith(attr, _) => catalog.sMap.map.contains(attr) | ||
| case IsNull(attr) => catalog.sMap.map.contains(attr) | ||
| case IsNotNull(attr) => catalog.sMap.map.contains(attr) |
There was a problem hiding this comment.
Addressing on next comment.
| def mergeUnion(other: RowKeyFilter): RowKeyFilter = { | ||
| other.points.foreach(p => points += p) | ||
|
|
||
| other.ranges.foreach { otherR => | ||
| var doesOverLap = false | ||
| ranges.foreach { r => | ||
| if (r.getOverLapScanRange(otherR) != null) { | ||
| r.mergeUnion(otherR) | ||
| doesOverLap = true | ||
| } | ||
| } | ||
| if (!doesOverLap) ranges += otherR |
There was a problem hiding this comment.
I have checked this with claude code and it's indeed an issue that also affects the spark3 module. I think we should address this on a separate PR, fixing it on both modules.
| if (scanRanges.isEmpty && points.isEmpty) { | ||
| regions.map { region => | ||
| val fullRange = Range(region) | ||
| HBaseInputPartition(region.index, Seq(fullRange), Seq.empty): InputPartition | ||
| } |
There was a problem hiding this comment.
Addressing on next commit.
| private def setStopRow(scan: Scan, bound: Bound): Scan = { | ||
| if (bound.inc) { | ||
| val incremented = Utils.incrementByteArray(bound.b) | ||
| if (incremented != null) scan.withStopRow(incremented) | ||
| else scan |
There was a problem hiding this comment.
Fixing in next commit.
| case class HBaseInputPartition( | ||
| index: Int, | ||
| scanRanges: Seq[Range], | ||
| points: Seq[Array[Byte]]) | ||
| extends InputPartition |
No description provided.