nsivabalan commented on PR #18076:
URL: https://github.com/apache/hudi/pull/18076#issuecomment-4827413119

   @lokeshj1703 — took the liberty (maintainer-can-modify is on) of pushing a 
follow-up commit to this branch addressing the BLOCKING + IMPORTANT items from 
my review. Happy to revert / split if you'd prefer a different shape.
   
   Commit: c9052af32102 — \`fix: Apply files-per-sync limit deterministically 
via window predicate\`
   
   ### What changed and why
   
   **1. Files-limit now rides the existing ordered window (addresses BLOCKING 
#1)**
   
   Previously: \`metadataRows = 
checkPointAndDataset.getRight().get().limit((int) numFilesLimit)\` followed by 
\`getCheckpointFromLastRow(metadataRows, ...)\`.
   
   The dataset returned by \`filterAndGenerateCheckpointBasedOnSourceLimit\` is 
not explicitly ordered immediately before \`.limit()\`, so on a multi-partition 
input Spark's \`GlobalLimit\` can pick an arbitrary subset. Recalculating the 
checkpoint from the max of that subset then points past skipped files → silent 
data loss on the next sync.
   
   Fix: in \`IncrSourceHelper.filterAndGenerateCheckpointBasedOnSourceLimit\` 
add a \`row_number()\` column alongside the existing \`cumulativeSize\` window, 
and filter on both predicates in a single window pass. The existing \`row = 
collectedRows.orderBy(...desc).first()\` then produces the correct checkpoint 
with no second pass.
   
   \`\`\`java
   WindowSpec windowSpec = Window.orderBy(col(orderColumn), col(keyColumn));
   Dataset<Row> aggregatedData = orderedDf
       .withColumn(CUMULATIVE_COLUMN_NAME, 
sum(col(limitColumn)).over(windowSpec))
       .withColumn(CUMULATIVE_COUNT_COLUMN_NAME, row_number().over(windowSpec));
   Dataset<Row> collectedRows = aggregatedData
       .filter(col(CUMULATIVE_COLUMN_NAME).leq(sourceLimit)
           .and(col(CUMULATIVE_COUNT_COLUMN_NAME).leq(numFilesLimit)));
   \`\`\`
   
   I kept the existing 4-arg \`filterAndGenerateCheckpointBasedOnSourceLimit\` 
signature as an overload that delegates with \`numFilesLimit = 
Long.MAX_VALUE\`, so no other callers (\`TestIncrSourceHelper\` has 9) need to 
change.
   
   **2. Removed the duplicate persist/unpersist block in \`CloudDataFetcher\` 
(addresses BLOCKING #2)**
   
   With #1 the post-hoc \`metadataRows.persist()\` block is no longer needed — 
the helper already manages persist/unpersist on \`sourceData\`. No more leak 
surface on exception.
   
   **3. Removed the \`cloudObjectMetadata.size() >= numFilesLimit\` recalc 
heuristic (addresses IMPORTANT #3)**
   
   \`getObjectMetadata\` dedupes via \`.distinct()\`, so this comparison was 
unreliable when duplicate events existed. With the limit applied in the window, 
the recalc branch isn't needed at all.
   
   **4. Removed \`IncrSourceHelper.getCheckpointFromLastRow\`**
   
   No longer called anywhere.
   
   **5. Reverted unrelated \`isNullOrEmpty → .isEmpty()\` cleanup in 
\`CloudObjectsSelectorCommon\` (NIT #8)**
   
   Kept the PR scoped to the fix.
   
   **6. Tests added in \`TestIncrSourceHelper\`** (addresses IMPORTANT #4):
   
   - \`testFilesLimitContiguousAcrossManyPartitions\` — 50 files spread across 
8 Spark partitions, iterate 5 syncs with \`numFilesLimit = 10\`, assert all 50 
files come out in strict insertion order with no gaps. This is the test that 
would have caught the \`.limit()\` non-determinism on a real cluster.
   - \`testFilesLimitCrossesCommitBoundary\` — files-limit truncates 
mid-commit; assert next sync resumes from the next file in the same commit, no 
skips or re-reads.
   - \`testFilesLimitBindingOverByteLimit\` — files-limit is binding while 
byte-limit is slack.
   - \`testFilesLimitLargerThanAvailable\` — files-limit > dataset; byte-path 
unaffected (guards the recalc-heuristic removal).
   
   The existing \`testFilesLimitCheckpointConsistency\` in 
\`TestS3EventsHoodieIncrSource\` still passes unchanged.
   
   ### Test runs (local)
   
   - \`TestIncrSourceHelper\`: 11 / 11 passing (7 pre-existing + 4 new).
   - \`TestS3EventsHoodieIncrSource\`: 16 / 16 passing.
   
   ### Not addressed in this commit (leaving for you / discussion)
   
   - **IMPORTANT #5 (GCS mirror test)** — \`TestGcsEventsHoodieIncrSource\` 
mirror of the S3 files-limit test. The behavior is covered at the helper layer 
by the new tests, but a GCS-path integration test would still be valuable. Let 
me know if you want me to add it.
   - **SUGGESTION #6 (default limit lowering)** — left at 10M as you had it. 
Reasonable people will disagree; happy to push a smaller default if you'd like.
   - **SUGGESTION #7 (log-message ordering)** — folded a single \`log.info\` 
showing both \`sourceLimit\` and \`numFilesLimit\` up-front, which seemed 
sufficient.
   
   Diff stats: 4 files, +175 / -50.
   
   Net effect on the PR: same user-facing config + behavior, deterministic 
across executor partitions, simpler control flow, no leak surface. Let me know 
if you want any of this reshaped.


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