prashantwason commented on PR #19946:
URL: https://github.com/apache/hudi/pull/19946#issuecomment-6052990577

   Thanks for this fix. We hit this in production: a COPY_ON_WRITE upsert with 
~1000 executors, where an async clean completed mid-write. After that, every 
executor request re-synced the view under the view lock, the timeline server 
stopped responding, and marker creation failed with `NoHttpResponseException` 
after ~10 min. The stage aborted after all task retries. The current version of 
this PR fixes that case. I had opened #20221 for the same issue before finding 
this PR, and I've closed it in favor of this one.
   
   One gap worth considering: the exemption reuses 
`shouldThrowExceptionIfLocalViewBehind`, which only covers **one** trailing 
clean. If two cleans complete after the client loaded its timeline:
   
   ```
   client: [c1]                  (hash H)
   server: [c1, clean2, clean3]
   ```
   
   the last instant is a clean, but the instants before it are `[c1, clean2]`, 
whose hash is not H. So `syncIfLocalViewBehind` still syncs on every request, 
and the final check then rejects with 400. Clients behind 
`PriorityBasedFileSystemView` fall back to their local view, but that still 
costs one full reload per executor, and marker requests have no fallback. 
Long-running writes with frequent async cleaning (e.g. from a table service 
manager) can realistically see more than one clean complete mid-write.
   
   A small extension that keeps the current behavior otherwise: walk back over 
all trailing cleans and compare the client hash against the timeline cut before 
each one. That also covers a client that has already seen some of the trailing 
cleans:
   
   ```java
   private boolean isLocalTimelineAheadOnlyByCleans(HoodieTimeline 
localTimeline, String timelineHashFromClient) {
     List<HoodieInstant> instants = localTimeline.getInstants();
     // walk back over the trailing cleans, newest first
     for (int i = instants.size() - 1; i >= 0 && 
instants.get(i).getAction().equals(HoodieTimeline.CLEAN_ACTION); i--) {
       if 
(localTimeline.findInstantsBefore(instants.get(i).requestedTime()).getTimelineHash().equals(timelineHashFromClient))
 {
         return true;
       }
     }
     return false;
   }
   ```
   
   `shouldThrowExceptionIfLocalViewBehind` could become 
`!isLocalTimelineAheadOnlyByCleans(...)` (for a non-empty timeline), so the 
sync decision and the final check keep using the same predicate, as they do in 
this PR. Non-clean differences, such as commits removed by a restore, still 
sync and fail the final check exactly as before. Only the "server has extra 
cleans" case is widened.
   
   A test that reproduces it (fails with 400 on master; the current head of 
this PR uses the same single-clean predicate):
   
   ```java
   HoodieTestTable testTable = HoodieTestTable.of(metaClient);
   testTable.addCommit("001");
   HoodieTimeline staleClientTimeline = 
metaClient.reloadActiveTimeline().filterCompletedAndCompactionInstants();
   requestLatestDataFiles(staleClientTimeline);           // server loads its 
view, no sync
   testTable.addClean("002");
   testTable.addClean("003");
   
requestLatestDataFiles(metaClient.reloadActiveTimeline().filterCompletedAndCompactionInstants());
 // server syncs once
   // 32 concurrent requests with the stale timeline: expect 0 syncs and no 400s
   ```
   
   Happy to send this as a follow-up after this PR merges if you'd rather keep 
this one narrow.
   


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