[ 
https://issues.apache.org/jira/browse/HUDI-8150?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17890770#comment-17890770
 ] 

Y Ethan Guo edited comment on HUDI-8150 at 10/21/24 10:33 PM:
--------------------------------------------------------------

In Hudi 1.0, we'll maintain the way how the Spark SQL writer and reader paths 
work regarding how the custom payload classes are used to fulfill specific DML 
logic.  Here are more details.  HUDI-8203 also needs the following information 
to use the merge mode and payload classes properly in the Spark SQL.

On master and existing behavior (before HUDI-8203): we don't write the payload 
class name to hoodie.properties if not set explicitly by the user.  The default 
payload class in 0.x releases is DefaultHoodieRecordPayload.

On the writer side in Spark SQL: there are two cases that are handled 
specifically: (1) deducing logic: INSERT INTO automatically deduces the custom 
payload class to use in certain cases, see code and details below; (2) MERGE 
INTO uses expression payload class (ExpressionPayload) for merge behavior 
defined by the SQL. The generated records are directly upserted to the table, 
and the reader honors the existing payload class for record merging.  In other 
cases, the writer path uses the payload classes directly if record merge is 
required.

 
{code:java}
val payloadClassName = if (useLegacyInsertDropDupFlow) {
  deducePayloadClassNameLegacy(operation, tableType, insertMode)
} else {
  if (insertDupPolicy == FAIL_INSERT_DUP_POLICY) {
    classOf[ValidateDuplicateKeyPayload].getCanonicalName
  } else {
    classOf[DefaultHoodieRecordPayload].getCanonicalName
  }
}private def deducePayloadClassNameLegacy(operation: String, tableType: String, 
insertMode: InsertMode): String = {
  if (operation == UPSERT_OPERATION_OPT_VAL &&
    tableType == COW_TABLE_TYPE_OPT_VAL && insertMode == InsertMode.STRICT) {
    // Validate duplicate key for COW, for MOR it will do the merge with the 
DefaultHoodieRecordPayload
    // on reading.
    // TODO use HoodieSparkValidateDuplicateKeyRecordMerger when 
SparkRecordMerger is default
    classOf[ValidateDuplicateKeyPayload].getCanonicalName
  } else if (operation == INSERT_OPERATION_OPT_VAL && tableType == 
COW_TABLE_TYPE_OPT_VAL &&
    insertMode == InsertMode.STRICT) {
    // Validate duplicate key for inserts to COW table when using strict insert 
mode.
    classOf[ValidateDuplicateKeyPayload].getCanonicalName
  } else {
    classOf[DefaultHoodieRecordPayload].getCanonicalName
  }
} {code}
 
 - payload class set (user sets it) in hoodie.properties -> Spark SQL writer 
uses it and skips payload deduction logic for INSERT INTO.  This means that 
INSERT INTO uses user specified payload class.  If the payload class is not 
explicitly set (using default payload), the payload deduction logic takes 
effect for INSERT INTO. MERGE INTO always uses ExpressionPayload.
 - payload class not set (using default) in hoodie.properties -> Spark SQL 
deduces the payload class (can be different from the default payload class) to 
use. MERGE INTO always uses ExpressionPayload regardless.

On the reader side in Spark SQL: the file group reader-based implementation 
based on merger classes or custom payload class is used (see HUDI-7678).  Any 
custom payload classes mentioned above (e.g., payload class for dedup, 
expression payload class) are never used on the reader side.

We'll still maintain the above merging behavior for Spark SQL.  With HUDI-8203 
introducing the merge mode to rule the merging behavior, we're writing the 
merge mode to the table config. There are some nuances to consider here around 
whether to write the inferred payload class based on the merge mode, for SQL 
writer merge behavior:
 - (fine with master and the HUDI-8203 PR) MIT should always use the expression 
payload, because MIT statement dictates the update/merge behavior.
 - (problem to solve for HUDI-82303) INSERT INTO has some complex logic on 
deducing internal payload to use
 -- master branch follows this
 -- In the HUDI-8203 PR, there is currently a conflict between payload config 
in table config vs the deducted payload, causing behavior change

 - (fine with master and the PR) All other DMLs use custom payload available in 
the table config.

In the new behavior / merge mode PR (HUDI-8203):
 - Always merge mode config into hoodie.properties

Proposal 1
 - Maintain the behavior, where we don't write the payload class name to 
hoodie.properties if not set explicitly.
 - Always set merge mode table config (CUSTOM: custom payload class is set 
always; forOVERWRITE_WITH_LATEST and EVENT_TIME_ORDERING we can infer the 
payload class from merge mode, i.e., we know the relationship between merge 
mode and payload class)
 - In table config,
  - For Spark SQL, do not add the payload class name when loading the 
HoodieTableConfig
  - For other places, still attach the payload from the Table Configs

Drawback: this adds more bandaid just for Spark SQL to get it working.  
Ideally, the table config should contain all the merge mode/payload classes to 
dictate the record merge behavior, which should not be changed once the table 
is created, unless the user wants to do one-time change or migration.  Adding 
bandaids around table config persistence and merging behavior just for Spark 
SQL makes it hard to reason going forward.

Proposal 2 (go with this)
 - (Prefer this) Always write the payload class config in hoodie.properties 
(implemented in the current HUDI-8203 PR)
 - For Spark SQL INSERT INTO,
  - for CUSTOM payload, always use it which is the current behavior
  - (change needed) if it's default hoodie payload, use deduction logic without 
honoring the table config.

Proposal 2 streamlined the table configs to be easy to understand; while having 
logic for Spark SQL write only for the cases where default hoodie payload is 
used, so the bandaid scope is much smaller, and easier to deprecate in the 
future.


was (Author: JIRAUSER280684):
In Hudi 1.0, we'll maintain the way how the Spark SQL writer and reader paths 
work regarding how the custom payload classes are used to fulfill specific DML 
logic.  Here are more details.  HUDI-8203 also needs the following information 
to use the merge mode and payload classes properly in the Spark SQL.

On master and existing behavior (before HUDI-8203): we don't write the payload 
class name to hoodie.properties if not set explicitly by the user.  The default 
payload class in 0.x releases is DefaultHoodieRecordPayload.

On the writer side in Spark SQL: there are two cases that are handled 
specifically: (1) deducing logic: INSERT INTO automatically deduces the custom 
payload class to use in certain cases, see code and details below; (2) MERGE 
INTO uses expression payload class (ExpressionPayload) for merge behavior 
defined by the SQL. The generated records are directly upserted to the table, 
and the reader honors the existing payload class for record merging.  In other 
cases, the writer path uses the payload classes directly if record merge is 
required.

 
{code:java}
val payloadClassName = if (useLegacyInsertDropDupFlow) {
  deducePayloadClassNameLegacy(operation, tableType, insertMode)
} else {
  if (insertDupPolicy == FAIL_INSERT_DUP_POLICY) {
    classOf[ValidateDuplicateKeyPayload].getCanonicalName
  } else {
    classOf[DefaultHoodieRecordPayload].getCanonicalName
  }
}private def deducePayloadClassNameLegacy(operation: String, tableType: String, 
insertMode: InsertMode): String = {
  if (operation == UPSERT_OPERATION_OPT_VAL &&
    tableType == COW_TABLE_TYPE_OPT_VAL && insertMode == InsertMode.STRICT) {
    // Validate duplicate key for COW, for MOR it will do the merge with the 
DefaultHoodieRecordPayload
    // on reading.
    // TODO use HoodieSparkValidateDuplicateKeyRecordMerger when 
SparkRecordMerger is default
    classOf[ValidateDuplicateKeyPayload].getCanonicalName
  } else if (operation == INSERT_OPERATION_OPT_VAL && tableType == 
COW_TABLE_TYPE_OPT_VAL &&
    insertMode == InsertMode.STRICT) {
    // Validate duplicate key for inserts to COW table when using strict insert 
mode.
    classOf[ValidateDuplicateKeyPayload].getCanonicalName
  } else {
    classOf[DefaultHoodieRecordPayload].getCanonicalName
  }
} {code}
 
 - payload class set (user sets it) in hoodie.properties -> Spark SQL writer 
uses it and skips payload deduction logic for INSERT INTO.  This means that 
INSERT INTO uses user specified payload class.  If the payload class is not 
explicitly set (using default payload), the payload deduction logic takes 
effect for INSERT INTO. MERGE INTO always uses 
 - payload class not set (using default) in hoodie.properties -> Spark SQL 
deducts the payload class (can be different from default) to use

On the reader side in Spark SQL:

HUDI-8203 SQL writer merge behavior:
 - (fine with master and the PR) MIT should always use the expression payload, 
because MIT statement dictates the update/merge behavior.
 - (problem to solve) INSERT INTO has some complex logic on determining 
internal payload to use

  - master branch follows this
  - In the PR, there is currently a conflict between payload config in table 
config vs the deducted payload, causing behavior change
 - (fine with master and the PR) All other DMLs use custom payload avaiable.

In the new behavior / merge mode PR:
 - Always merge mode config into hoodie.properties

Proposal 1
 - Maintain the behavior, where we don't write the payload class name to 
hoodie.properties if not set explicitly.
 - always set merge mode table config others (CUSTOM: custom payload class is 
set always, and OVERWRITE_WITH_LATEST and EVENT_TIME_ORDERING we can infer from 
merge mode, i.e., we know the relationship between merge mode and payload class)
 - In table config,
  - For Spark SQL, do not add the payload class name when loading the 
HoodieTableConfig
  - For other places, still attach the payload from the Table Configs

Proposal 2 (go with this)
 - (Prefer this) Always write the payload class config in hoodie.properties 
(what in the current PR)
 - For Spark SQL INSERT INTO,
  - for CUSTOM payload, always use it which is the current behavior
  - (change needed) if it's default hoodie payload, use deduction logic without 
honoring the table config

> Review semantics of SQL and RecordMerger interplay
> --------------------------------------------------
>
>                 Key: HUDI-8150
>                 URL: https://issues.apache.org/jira/browse/HUDI-8150
>             Project: Apache Hudi
>          Issue Type: New Feature
>            Reporter: Ethan Guo (this is the old account; please use "yihua")
>            Assignee: Y Ethan Guo
>            Priority: Blocker
>             Fix For: 1.0.0
>
>




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to