ashokkumar-allu opened a new issue, #19281:
URL: https://github.com/apache/hudi/issues/19281

   ### Feature Description
   
   **What the feature achieves:**
   Add first-class support for reading **multiple Hudi datasets incrementally 
in a single Hudi
   Streamer (DeltaStreamer) job** and merging them with a single SQL 
transformation — atomically,
   under one checkpoint and one commit.
   
   Concretely this introduces:
   - **`HoodieIncrMultiSource`** — a new Streamer source that reads N Hudi 
tables incrementally in
     one batch, each table with its own independent checkpoint, 
`numInstantsPerFetch`, missing-
     checkpoint strategy, hollow-commit handling and timeline management 
(reusing `HoodieIncrSource`
     semantics per table).
   - **`MultiDatasetTransformer`** — a transformer interface that accepts 
`List<Dataset<Row>>`;
     `SqlFileBasedTransformer` implements it and lets a SQL file reference each 
source by index as
     `<SRC_0>`, `<SRC_1>`, … (with `<SRC>` kept as an alias for the first, for 
backward compatibility).
   - **`MultiTableCheckpointManager`** — a per-table checkpoint format
     (`table1=ckpt1,table2=ckpt2`) stored in `deltastreamer.checkpoint.key`, 
fully backward
     compatible with the legacy single-timestamp format.
   - **Multi-dataset `InputBatch`** — carries a `List<Dataset<Row>>` via 
`getBatches()`; `getBatch()`
     is unchanged for single-dataset sources.
   
   **Why this feature is needed:**
   Today Hudi has no single-query mechanism for incremental reads across 
multiple tables. Users must
   either run **one DeltaStreamer job per source table and merge downstream**, 
or fall back to
   **full snapshot reads** for all-but-one table. Both are problematic:
   
   - **Data inconsistency / silent failures** — separate jobs drift in time 
(Table A at T+10, Table B
     at T+7), producing incomplete/misleading joined output that only backfills 
can fix.
   - **No atomicity** — there is no transaction boundary across N jobs; a 
partial success leaves the
     unified table half-updated.
   - **Operational overhead** — N watermarks to reconcile (watermark drift 
makes replay/debugging
     hard), and N pipelines to monitor and alert on.
   - **Snapshot-read workaround is expensive** — re-reading the last X days of 
every other table on
     each run burns far more compute than a true incremental read.
   
   **Expected improvement:**
   - Fewer data-inconsistency-driven backfill incidents on multi-source 
pipelines.
   - Lower end-to-end data lag for multi-source ingestion.
   - Lower total compute vs. the snapshot-read workaround.
   
   **Non-goals:**
   - Does **not** auto-resolve join/merge semantics — users still own join keys 
and conflict
     resolution (outer joins, merge payloads).
   - Does **not** change Hudi's write/commit mechanism — this is confined to 
source reading and
     transformation inside Hudi Streamer.
   
   ### User Experience
   
   ## **How users will use this feature:**
   Users opt in purely through Streamer configuration + a SQL file. No code 
changes required.
   
   - Configuration changes needed
   ### 1. Point the Streamer at the new source
   ```properties
   
hoodie.deltastreamer.source.class=org.apache.hudi.utilities.sources.HoodieIncrMultiSource
   ```
   
   ### 2. Declare the source tables and per-table settings
   ```properties
   # Ordered list of source tables (order maps to <SRC_0>, <SRC_1>, … in SQL)
   hoodie.streamer.source.multi.hudi.tables=db.orders,db.users
   
   # Per-table config. Dots in a table name become underscores in the property 
key;
   # the original dotted name is preserved inside the checkpoint.
   hoodie.streamer.source.hudi.db_orders.path=/hudi/orders
   hoodie.streamer.source.hudi.db_orders.num_instants=5
   hoodie.streamer.source.hudi.db_orders.missing_checkpoint_strategy=READ_LATEST
   
   hoodie.streamer.source.hudi.db_users.path=/hudi/users
   hoodie.streamer.source.hudi.db_users.num_instants=3
   
hoodie.streamer.source.hudi.db_users.missing_checkpoint_strategy=READ_UPTO_LATEST_COMMIT
   
   # Multi-table checkpointing (default true). false => legacy 
single-checkpoint behavior.
   hoodie.streamer.source.multi.hudi.checkpoint.enable=true
   ```
   
   ### 3. Merge the datasets with a SQL file
   ```properties
   
hoodie.deltastreamer.transformer.class=org.apache.hudi.utilities.transform.SqlFileBasedTransformer
   hoodie.deltastreamer.transformer.sql.file=/path/to/multi_table_transform.sql
   ```
   ```sql
   -- multi_table_transform.sql : <SRC_0> = db.orders, <SRC_1> = db.users
   -- OUTER JOIN is recommended so a table with no new data in this batch never 
drops rows.
   SELECT
       o.order_id, o.amount, o.user_id,
       u.user_name, u.email
   FROM <SRC_0> o
   FULL OUTER JOIN <SRC_1> u
       ON o.user_id = u.user_id;
   ```
   
   ### Checkpoint (stored in `deltastreamer.checkpoint.key`, one atomic commit)
   ```
   Multi-table  : "db.orders=20250606182826197,db.users=20250606182830145"
   Legacy/single: "20250606182826197"   (still read/written when multi-table 
checkpoint is disabled)
   ```
   
   ## API changes
   API changes are all additive / backward compatible
     - `InputBatch#getBatches(): List<T>` — new; `getBatch()` unchanged for 
single-dataset sources
       (it now fails loud if called on a multi-dataset batch, rather than 
silently dropping datasets).
     - `MultiDatasetTransformer` — new interface: `apply(jsc, spark, 
List<Dataset<Row>>, props)`.
     - `SqlFileBasedTransformer` — now implements `MultiDatasetTransformer`; 
adds `<SRC_0>`,`<SRC_1>`,…
       placeholders alongside the existing `<SRC>`.
   
   ## Usage examples
     - Provided in config section
   
   ## Guardrails
   - Startup validation: `HoodieIncrMultiSource` **requires** a 
`MultiDatasetTransformer` (e.g.
     `SqlFileBasedTransformer`, directly or inside a `ChainedTransformer`). 
Misconfiguration fails
     the job at startup instead of silently writing only the last table.
   - Every configured table always yields a dataset each batch — an empty, 
correctly-schema'd
     dataset when there's no new data — so `LEFT/FULL OUTER JOIN`s always have 
a valid relation.
   
   ## Guidance
   - Prefer **OUTER JOIN** on incremental sources (inner join can silently miss 
delayed data).
   - For overlapping columns / partial-field updates, use a **merge payload** 
(e.g. a
     `HoodieGenericMergePayload` or a custom payload) rather than 
last-writer-wins overwrite.
   
   ### Hudi RFC Requirements
   
   **RFC PR link:** (if applicable)
   Work in progress
   **Why RFC is/isn't needed:** 
   - Does this change public interfaces/APIs? (Yes)
   - Does this change storage format? (No)
   - Justification:
   This introduces a **new Hudi Streamer source** and **new public interfaces** 
(`HoodieIncrMultiSource`,
   `MultiDatasetTransformer`, `InputBatch#getBatches()`) plus a new multi-table 
checkpoint encoding —
   per the RFC process, new Streamer sources and changes to public interfaces 
warrant an RFC.
   
   - It does **not** change the on-disk table/storage format. The only 
serialized change is the
     contents of `deltastreamer.checkpoint.key` in the Streamer's own `.commit` 
metadata, which
     remains backward compatible: legacy single-value checkpoints are read and 
honored, and the
     multi-table format degrades to the legacy format when 
`...checkpoint.enable=false`.
   - All Java API changes are additive; existing single-source pipelines and 
non-SQL transformers
     are unaffected.


-- 
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