voonhous commented on code in PR #19849:
URL: https://github.com/apache/hudi/pull/19849#discussion_r3944193962


##########
hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieBaseRelation.scala:
##########
@@ -75,16 +74,6 @@ trait HoodieFileSplit {}
 
 case class HoodieTableSchema(structTypeSchema: StructType, schema: 
HoodieSchema, internalSchema: Option[InternalSchema] = None)
 
-case class HoodieTableState(tablePath: String,
-                            latestCommitTimestamp: Option[String],
-                            recordKeyField: String,
-                            orderingFields: List[String],
-                            usesVirtualKeys: Boolean,
-                            metadataConfig: HoodieMetadataConfig,
-                            recordMergeImplClasses: List[String],
-                            recordMergeStrategyId: String)

Review Comment:
   `HoodieTableState` is plain `public` and ships in hudi-spark-bundle, so any 
out-of-repo relation subclass, custom datasource or tool that constructs one or 
reads `relation.tableState` breaks at compile time against the new source, and 
with `NoClassDefFoundError`/`NoSuchMethodError` against the new bundle. The PR 
body says "No public API or behavior change".
   
   Its file-neighbour at `HoodieMergeOnReadRDDV2.scala:80` is `private[hudi] 
case class HoodieMergeOnReadBaseFileReaders`, so the codebase does scope 
internal case classes deliberately; this one never was.
   
   Could we either keep the case class for one release marked deprecated, or 
drop the "no public API change" claim from the description and flag the removal 
in the release notes?



##########
hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieMergeOnReadRDDV2.scala:
##########
@@ -89,7 +89,7 @@ private[hudi] case class 
HoodieMergeOnReadBaseFileReaders(fullSchemaReader: Base
  * @param fileReaders            suite of base file readers
  * @param tableSchema            table's full schema
  * @param requiredSchema         expected (potentially) projected schema
- * @param tableState             table's state
+ * @param latestCommitTimestamp latest completed commit timestamp for the query

Review Comment:
   The value passed here is 
`specifiedQueryTimestamp.orElse(timeline.lastInstant())`, which is not the 
latest completed commit at two of the three call sites:
   
   - under time travel (`as.of.instant` / 
`hoodie.datasource.read.begin.instanttime`) it is the user's as-of instant;
   - `MergeOnReadIncrementalRelationV1` overrides `timeline` to 
`findInstantsInRange(startTimestamp, endTimestamp)` plus pending compaction, 
and V2 to `queryContext.getActiveTimeline`, so it is the last instant of the 
queried window.
   
   Could we word it as the query's target instant -- the as-of instant, or the 
last instant of the queried timeline -- instead? As written, a reader will 
assume the table's newest commit is what reaches 
`HoodieFileGroupReader.withLatestCommitTime` and mis-reason about merge and 
log-block visibility.



##########
hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieBaseRelation.scala:
##########
@@ -252,19 +241,7 @@ abstract class HoodieBaseRelation(val sqlContext: 
SQLContext,
     HoodieFileIndex(sparkSession, metaClient, Some(tableStructSchema), 
optParams,
       FileStatusCache.getOrCreate(sparkSession), shouldIncludeLogFiles())
 
-  lazy val tableState: HoodieTableState = {
-    val recordMergerImpls = 
optParams.get(HoodieWriteConfig.RECORD_MERGE_IMPL_CLASSES.key()).map(impls => 
ConfigUtils.split2List(impls).asScala.toList).getOrElse(List.empty)
-    // Subset of the state of table's configuration as of at the time of the 
query
-    HoodieTableState(tablePath = basePath.toString,
-      latestCommitTimestamp = queryTimestamp,
-      recordKeyField = recordKeyField,
-      orderingFields = orderingFields,
-      usesVirtualKeys = !tableConfig.populateMetaFields(),
-      metadataConfig = fileIndex.getMetadataConfig,

Review Comment:
   Separately: dropping this call leaves 
`BaseHoodieTableFileIndex#getMetadataConfig` with no in-repo production caller. 
The only remaining reference is 
`BaseHoodieTableFileIndexTest#testGetMetadataConfigReturnsFieldValue`, which 
reflectively sets the field and then asserts the getter hands it back.
   
   Its neighbours in that class are already scoped -- 
`@Getter(AccessLevel.PROTECTED)` on `partitionColumns` and `queryPaths`. Could 
we drop `metadataConfig`'s `@Getter` to `AccessLevel.PROTECTED` and retire that 
test with it?



##########
hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieBaseRelation.scala:
##########
@@ -252,19 +241,7 @@ abstract class HoodieBaseRelation(val sqlContext: 
SQLContext,
     HoodieFileIndex(sparkSession, metaClient, Some(tableStructSchema), 
optParams,
       FileStatusCache.getOrCreate(sparkSession), shouldIncludeLogFiles())
 
-  lazy val tableState: HoodieTableState = {
-    val recordMergerImpls = 
optParams.get(HoodieWriteConfig.RECORD_MERGE_IMPL_CLASSES.key()).map(impls => 
ConfigUtils.split2List(impls).asScala.toList).getOrElse(List.empty)
-    // Subset of the state of table's configuration as of at the time of the 
query
-    HoodieTableState(tablePath = basePath.toString,
-      latestCommitTimestamp = queryTimestamp,
-      recordKeyField = recordKeyField,
-      orderingFields = orderingFields,
-      usesVirtualKeys = !tableConfig.populateMetaFields(),
-      metadataConfig = fileIndex.getMetadataConfig,

Review Comment:
   This call was the only thing forcing the `lazy val fileIndex` (line 240), 
whose own scaladoc warns it "initializes eagerly listing all of the files w/in 
the given Hudi table".
   
   For `MergeOnReadIncrementalRelationV1`/`V2` on the non-`fullTableScan` 
branch, `collectFileSplits` builds its own `HoodieTableFileSystemView` from 
`affectedFilesInCommits` and never touches `fileIndex` -- the only `fileIndex` 
hits in those two files are V1:133 and V2:127, inside `listFileSplits`, a 
different entry point. So an incremental MOR query with a start/end range used 
to perform a full table listing at `composeRDD` time and now does not.
   
   That reads like a win, but it is a real behavior delta against the 
description, and it changes when the listing and any listing-time failure 
surface. Could we call it out in the PR body and add an incremental-MOR test 
that pins the new behavior?



##########
hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieMergeOnReadRDDV2.scala:
##########
@@ -89,7 +89,7 @@ private[hudi] case class 
HoodieMergeOnReadBaseFileReaders(fullSchemaReader: Base
  * @param fileReaders            suite of base file readers
  * @param tableSchema            table's full schema
  * @param requiredSchema         expected (potentially) projected schema
- * @param tableState             table's state
+ * @param latestCommitTimestamp latest completed commit timestamp for the query

Review Comment:
   Nit: every other entry aligns its description at a fixed column and this one 
uses a single space, so the block no longer reads as a table.
   
   While we are in here, the block is also missing `sqlConf`, 
`optionalFilters`, `metaClient` and `options`, all present in the constructor 
before this PR. Could we add them?



##########
hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieMergeOnReadRDDV2.scala:
##########
@@ -100,7 +100,7 @@ class HoodieMergeOnReadRDDV2(@transient sc: SparkContext,
                              fileReaders: HoodieMergeOnReadBaseFileReaders,
                              tableSchema: HoodieTableSchema,
                              requiredSchema: HoodieTableSchema,
-                             tableState: HoodieTableState,
+                             latestCommitTimestamp: Option[String],

Review Comment:
   All three uses inside `compute()` are `latestCommitTimestamp.orNull` (lines 
211, 229, 242), so the `Some`/`None` distinction is never read. Both builders 
then do `ValidationUtils.checkArgument(latestCommitTime != null, "Latest commit 
time is required")` (`HoodieFileGroupReader.java:121`, 
`HoodieLsmFileGroupReader.java:108`), so a `None` fails per-task on the 
executor rather than on the driver.
   
   Since the PR is deliberately picking this parameter's type, could it be 
`String`, resolved once on the driver? That removes the wrapper and turns an 
executor-side `IllegalArgumentException` into a fail-fast at relation 
construction.



##########
hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/MergeOnReadIncrementalRelationV2.scala:
##########
@@ -271,4 +271,3 @@ trait HoodieIncrementalRelationV2Trait extends 
HoodieBaseRelation {
     optParams.getOrElse(DataSourceReadOptions.INCR_PATH_GLOB.key, 
DataSourceReadOptions.INCR_PATH_GLOB.defaultValue)
 
 }
-

Review Comment:
   This has nothing to do with collapsing `HoodieTableState` -- could we 
restore the trailing blank line? It widens the conflict surface for anyone 
cherry-picking onto release-1.2.x, and `git diff --check`, cited in the PR body 
as the verification, does not catch it.



##########
hudi-spark-datasource/hudi-spark-common/src/main/scala/org/apache/hudi/HoodieBaseRelation.scala:
##########
@@ -252,19 +241,7 @@ abstract class HoodieBaseRelation(val sqlContext: 
SQLContext,
     HoodieFileIndex(sparkSession, metaClient, Some(tableStructSchema), 
optParams,
       FileStatusCache.getOrCreate(sparkSession), shouldIncludeLogFiles())
 
-  lazy val tableState: HoodieTableState = {
-    val recordMergerImpls = 
optParams.get(HoodieWriteConfig.RECORD_MERGE_IMPL_CLASSES.key()).map(impls => 
ConfigUtils.split2List(impls).asScala.toList).getOrElse(List.empty)
-    // Subset of the state of table's configuration as of at the time of the 
query
-    HoodieTableState(tablePath = basePath.toString,
-      latestCommitTimestamp = queryTimestamp,
-      recordKeyField = recordKeyField,
-      orderingFields = orderingFields,
-      usesVirtualKeys = !tableConfig.populateMetaFields(),
-      metadataConfig = fileIndex.getMetadataConfig,
-      recordMergeImplClasses = recordMergerImpls,
-      recordMergeStrategyId = tableConfig.getRecordMergeStrategyId
-    )
-  }
+  protected lazy val latestCommitTimestamp: Option[String] = queryTimestamp

Review Comment:
   This is a second name for something the same class already exposes. 
`queryTimestamp` (line 260) is a `def` recomputed from the overridable 
`timeline` on every call, and `listLatestFileSlices` (line 345) uses it that 
way; as a `lazy val`, `latestCommitTimestamp` freezes the first value. The two 
disagree whenever the metaClient timeline is reloaded between 
`collectFileSplits` and `composeRDD` on a reused relation.
   
   It also lands on `HoodieBaseRelation`, so `BaseFileOnlyRelation`, 
`HoodieBootstrapRelation` and `IncrementalRelation` all inherit a member only 
the three MOR relations use, which widens the base class in a PR aimed at 
narrowing surface.
   
   Could we widen `private def queryTimestamp` to `protected def 
queryTimestamp` and pass `queryTimestamp` at the three call sites instead? That 
adds no member at all and keeps one accessor for the concept.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to