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


##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/table/format/cdc/CdcImageManager.java:
##########
@@ -105,13 +110,26 @@ private ExternalSpillableMap<String, byte[]> 
loadImageRecords(
         serializer.serialize(row, new BytesArrayOutputView(baos));
         imageRecordsMap.put(recordKey, baos.toByteArray());
       }
+    } catch (IOException | RuntimeException | Error e) {
+      closeSuppressing(imageRecordsMap, e);
+      throw e;
     }
     return imageRecordsMap;
   }
 
+  private static void closeSuppressing(

Review Comment:
   Addressed in adb73870b52e: CdcImageManager.close now attempts every 
cached-map close, preserves the first failure with later failures suppressed, 
and clears the cache in finally. Added coverage for continued cleanup and cache 
clearing.



##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/table/format/cdc/CdcImageManager.java:
##########
@@ -105,13 +110,26 @@ private ExternalSpillableMap<String, byte[]> 
loadImageRecords(
         serializer.serialize(row, new BytesArrayOutputView(baos));
         imageRecordsMap.put(recordKey, baos.toByteArray());
       }
+    } catch (IOException | RuntimeException | Error e) {
+      closeSuppressing(imageRecordsMap, e);
+      throw e;
     }
     return imageRecordsMap;
   }
 
+  private static void closeSuppressing(
+      ExternalSpillableMap<String, byte[]> imageRecordsMap,

Review Comment:
   Addressed in adb73870b52e: renamed the helper parameter to spillableMap so 
the signature is self-contained.



##########
hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/cdc/TestCdcImageManager.java:
##########
@@ -154,6 +154,55 @@ void testImageCacheReuseEvictionAndClose() throws 
IOException {
     }
   }
 
+  @Test
+  void testLoadClosesImageCacheWhenIteratorCreationFails() {
+    HoodieWriteConfig writeConfig = mock(HoodieWriteConfig.class);
+    when(writeConfig.getBasePath()).thenReturn("/table");
+    ExternalSpillableMap<String, byte[]> imageCache = mockImageCache();
+    RuntimeException failure = new RuntimeException("iterator creation 
failed");
+    CdcImageManager imageManager = new CdcImageManager(
+        rowType("value"),
+        writeConfig,
+        split -> {
+          throw failure;
+        });
+
+    try (MockedStatic<FormatUtils> mockedFormatUtils = 
mockStatic(FormatUtils.class)) {
+      mockedFormatUtils.when(() -> FormatUtils.spillableMap(
+          writeConfig, 1024L, CdcImageManager.class.getSimpleName()))
+          .thenReturn(imageCache);
+
+      assertSame(failure, assertThrows(
+          RuntimeException.class,
+          () -> imageManager.getOrLoadImages(1024L, fileSlice("001"))));
+      verify(imageCache).close();
+    }
+  }
+
+  @Test
+  void testLoadClosesIteratorAndImageCacheWhenIterationFails() {
+    HoodieWriteConfig writeConfig = mock(HoodieWriteConfig.class);
+    when(writeConfig.getBasePath()).thenReturn("/table");
+    ExternalSpillableMap<String, byte[]> imageCache = mockImageCache();
+    ClosableIterator<RowData> iterator = mock(ClosableIterator.class);

Review Comment:
   Addressed in adb73870b52e: extracted mockIterator with 
SuppressWarnings("unchecked") and use it in the parameterized test.



##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/table/format/cdc/CdcImageManager.java:
##########
@@ -105,13 +110,26 @@ private ExternalSpillableMap<String, byte[]> 
loadImageRecords(
         serializer.serialize(row, new BytesArrayOutputView(baos));
         imageRecordsMap.put(recordKey, baos.toByteArray());
       }
+    } catch (IOException | RuntimeException | Error e) {
+      closeSuppressing(imageRecordsMap, e);
+      throw e;
     }
     return imageRecordsMap;
   }
 
+  private static void closeSuppressing(
+      ExternalSpillableMap<String, byte[]> imageRecordsMap,
+      Throwable primary) {
+    try {
+      imageRecordsMap.close();
+    } catch (RuntimeException | Error closeError) {
+      primary.addSuppressed(closeError);

Review Comment:
   Addressed in adb73870b52e: added testLoadSuppressesImageCacheCloseError, 
which verifies that the original load failure is rethrown and the close failure 
is attached as its suppressed exception.



##########
hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/cdc/TestCdcImageManager.java:
##########
@@ -154,6 +154,55 @@ void testImageCacheReuseEvictionAndClose() throws 
IOException {
     }
   }
 
+  @Test
+  void testLoadClosesImageCacheWhenIteratorCreationFails() {
+    HoodieWriteConfig writeConfig = mock(HoodieWriteConfig.class);
+    when(writeConfig.getBasePath()).thenReturn("/table");
+    ExternalSpillableMap<String, byte[]> imageCache = mockImageCache();
+    RuntimeException failure = new RuntimeException("iterator creation 
failed");
+    CdcImageManager imageManager = new CdcImageManager(
+        rowType("value"),
+        writeConfig,
+        split -> {
+          throw failure;
+        });
+
+    try (MockedStatic<FormatUtils> mockedFormatUtils = 
mockStatic(FormatUtils.class)) {
+      mockedFormatUtils.when(() -> FormatUtils.spillableMap(
+          writeConfig, 1024L, CdcImageManager.class.getSimpleName()))
+          .thenReturn(imageCache);
+
+      assertSame(failure, assertThrows(
+          RuntimeException.class,
+          () -> imageManager.getOrLoadImages(1024L, fileSlice("001"))));
+      verify(imageCache).close();
+    }
+  }
+
+  @Test
+  void testLoadClosesIteratorAndImageCacheWhenIterationFails() {

Review Comment:
   Addressed in adb73870b52e: collapsed the duplicate failure tests into one 
parameterized test over iterator-creation and iteration failures.



##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/table/format/cdc/CdcIterators.java:
##########
@@ -347,8 +347,9 @@ public RowData next() {
 
     @Override
     public void close() {
-      logRecordIterator.close();
-      imageManager.close();
+      try (CdcImageManager ignored = imageManager) {
+        logRecordIterator.close();
+      }
     }

Review Comment:
   Addressed in adb73870b52e: added a comment explaining that the per-split 
close is required because the iterator mutates cached before-images and 
same-instant LOG_FILE slices share the cache key, so the next split must reload.



##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/table/format/cdc/CdcImageManager.java:
##########
@@ -86,7 +86,12 @@ public ExternalSpillableMap<String, byte[]> getOrLoadImages(
       cache.remove(oldest).close();
     }
     ExternalSpillableMap<String, byte[]> images = 
loadImageRecords(maxCompactionMemoryInBytes, fileSlice);
-    cache.put(instant, images);
+    try {
+      cache.put(instant, images);
+    } catch (RuntimeException | Error e) {
+      closeSuppressing(images, e);
+      throw e;
+    }

Review Comment:
   Addressed in adb73870b52e: removed the unreachable TreeMap.put try/catch.



##########
hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/cdc/TestCdcImageManager.java:
##########
@@ -154,6 +154,55 @@ void testImageCacheReuseEvictionAndClose() throws 
IOException {
     }
   }
 
+  @Test
+  void testLoadClosesImageCacheWhenIteratorCreationFails() {
+    HoodieWriteConfig writeConfig = mock(HoodieWriteConfig.class);
+    when(writeConfig.getBasePath()).thenReturn("/table");
+    ExternalSpillableMap<String, byte[]> imageCache = mockImageCache();
+    RuntimeException failure = new RuntimeException("iterator creation 
failed");
+    CdcImageManager imageManager = new CdcImageManager(
+        rowType("value"),
+        writeConfig,
+        split -> {
+          throw failure;
+        });
+
+    try (MockedStatic<FormatUtils> mockedFormatUtils = 
mockStatic(FormatUtils.class)) {
+      mockedFormatUtils.when(() -> FormatUtils.spillableMap(
+          writeConfig, 1024L, CdcImageManager.class.getSimpleName()))
+          .thenReturn(imageCache);
+
+      assertSame(failure, assertThrows(
+          RuntimeException.class,
+          () -> imageManager.getOrLoadImages(1024L, fileSlice("001"))));
+      verify(imageCache).close();

Review Comment:
   Addressed in adb73870b52e: the parameterized test now uses times(1), calls 
imageManager.close, and verifies the failed map is not closed again, proving it 
was not cached.



##########
hudi-flink-datasource/hudi-flink/src/main/java/org/apache/hudi/table/format/cdc/CdcIterators.java:
##########
@@ -287,15 +287,15 @@ public DataLogFileIterator(
           metaClient.getTableConfig().getPartialUpdateMode());
       this.logRecordIterator = logRecordIterator;
       this.deleteContext = new DeleteContext(props, 
tableSchema).withReaderSchema(tableSchema);
-      initImages(cdcFileSplit, writeConfig);
+      initImages(cdcFileSplit);

Review Comment:
   Addressed in adb73870b52e: both LOG_FILE construction call sites now close 
recordIterator on constructor or argument-evaluation failure while suppressing 
cleanup errors. BeforeImageIterator also closes its already-open cdcItr if 
image initialization fails.



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