danny0405 commented on code in PR #20486:
URL: https://github.com/apache/hudi/pull/20486#discussion_r4450431090


##########
hudi-common/src/main/java/org/apache/hudi/common/util/KafkaCheckpointUtils.java:
##########
@@ -109,28 +176,28 @@ public static Map<Integer, Long> 
parseCheckpoint(CheckpointFormat format, String
   }
 
   /**
-   * Calculate offset difference between two checkpoints.
-   * Handles partition additions, removals, and resets.
+   * Compare two checkpoints while preserving whether the resulting offset 
difference is exact.
    *
-   * Algorithm:
-   * 1. For each partition in current checkpoint:
-   *    - If partition exists in previous: diff = current - previous
-   *    - If partition is new: skip (start offset unknown, would overcount)
-   *    - If diff is negative (reset): skip (start offset unknown, would 
overcount)
-   * 2. Sum all partition diffs
+   * <p>Offsets for partitions present in both checkpoints are included when 
they move forward or
+   * remain unchanged. Partitions with an unknown start offset are reported 
separately and excluded
+   * from the known difference.</p>
    *
    * @param format Checkpoint format
    * @param previousCheckpoint Previous checkpoint string
    * @param currentCheckpoint Current checkpoint string
-   * @return Total offset difference across all partitions
+   * @return Offset comparison result containing the known difference and 
incomparable partitions
    */
-  public static long calculateOffsetDifference(CheckpointFormat format,
-                                                String previousCheckpoint,
-                                                String currentCheckpoint) {
+  public static OffsetComparisonResult compareOffsets(CheckpointFormat format,
+                                                       String 
previousCheckpoint,
+                                                       String 
currentCheckpoint) {
     Map<Integer, Long> previousOffsets = parseCheckpoint(format, 
previousCheckpoint);
     Map<Integer, Long> currentOffsets = parseCheckpoint(format, 
currentCheckpoint);
 
     long totalDiff = 0;
+    Set<Integer> addedPartitions = new TreeSet<>();
+    Set<Integer> resetPartitions = new TreeSet<>();
+    Set<Integer> missingPartitions = new TreeSet<>(previousOffsets.keySet());
+    missingPartitions.removeAll(currentOffsets.keySet());

Review Comment:
   Could we iterate previousOffsets.keySet() and add only partitions absent 
from currentOffsets to missingPartitions? Building a TreeSet of all previous 
partitions adds O(P log P) work and P temporary tree nodes on every comparison, 
even when the partition sets are identical and the result is empty. Collecting 
only missing IDs would preserve sorted diagnostics while keeping the common 
case linear. This is a small performance concern; end-to-end impact has not 
been benchmarked.



##########
hudi-common/src/main/java/org/apache/hudi/common/util/KafkaCheckpointUtils.java:
##########
@@ -56,6 +59,70 @@
 @Slf4j
 public class KafkaCheckpointUtils {
 
+  /**
+   * Result of comparing offsets from two checkpoints.
+   *
+   * <p>The known offset difference only includes partitions whose offsets can 
be compared
+   * exactly. A comparison is incomplete when either checkpoint has no 
partition offsets,
+   * partitions were added or removed, or an offset reset is detected, since 
the checkpoints
+   * do not contain enough information to calculate the full offset 
difference.</p>
+   */
+  public static final class OffsetComparisonResult {
+    private final long knownOffsetDifference;
+    private final boolean previousCheckpointHasOffsets;
+    private final boolean currentCheckpointHasOffsets;
+    private final Set<Integer> addedPartitions;
+    private final Set<Integer> resetPartitions;
+    private final Set<Integer> missingPartitions;
+
+    private OffsetComparisonResult(long knownOffsetDifference,
+                                   boolean previousCheckpointHasOffsets,
+                                   boolean currentCheckpointHasOffsets,
+                                   Set<Integer> addedPartitions,
+                                   Set<Integer> resetPartitions,
+                                   Set<Integer> missingPartitions) {
+      this.knownOffsetDifference = knownOffsetDifference;
+      this.previousCheckpointHasOffsets = previousCheckpointHasOffsets;
+      this.currentCheckpointHasOffsets = currentCheckpointHasOffsets;
+      this.addedPartitions = Collections.unmodifiableSet(new 
TreeSet<>(addedPartitions));
+      this.resetPartitions = Collections.unmodifiableSet(new 
TreeSet<>(resetPartitions));
+      this.missingPartitions = Collections.unmodifiableSet(new 
TreeSet<>(missingPartitions));

Review Comment:
   These three defensive copies look unnecessary with the current ownership 
model: this constructor is private, and its only caller creates the sets 
locally and never mutates or exposes them afterward. Could we wrap the supplied 
sets directly with Collections.unmodifiableSet(...) instead? That would retain 
the immutable public view while avoiding a second set allocation and copying 
the entries.



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