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


##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieAppendHandle.java:
##########
@@ -531,6 +532,7 @@ protected void doWrite(HoodieRecord record, HoodieSchema 
schema, TypedProperties
     } catch (Throwable t) {
       log.error("Error writing record " + record, t);
       if (!config.getIgnoreWriteFailed()) {
+        closeLogWriterQuietly(t);

Review Comment:
   Fixed by marking the append handle closed on failure before releasing the 
writer. A later `close()` returns without flushing the buffered records again. 
`testFailedFlushClosesWriterAndPreventsAnotherFlush` covers this with buffered 
records and with both successful and failing writer cleanup.



##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieBinaryCopyHandle.java:
##########
@@ -118,14 +118,26 @@ public void write() {
       log.info("Schema evolution enabled for binary copy: {}", 
schemaEvolutionEnabled);
       records = this.writer.binaryCopy(inputFiles, 
Collections.singletonList(path), writeScheMessageType, schemaEvolutionEnabled);
     } catch (IOException e) {
+      closeWriterAfterFailure(e);
       throw new HoodieIOException(e.getMessage(), e);
+    } catch (RuntimeException e) {
+      closeWriterAfterFailure(e);
+      throw e;
     } finally {
       this.recordsWritten = records;
       this.insertRecordsWritten = records;
     }
     log.info("Finish rewriting " + this.path + ". Using " + timer.endTimer() + 
" mills");
   }
 
+  private void closeWriterAfterFailure(Throwable failure) {
+    try {
+      this.writer.close();

Review Comment:
   Updated to `CloseableUtils.closeSuppressing(writer::close, failure)`, using 
the existing common utility after the rebase. It preserves the original failure 
and suppresses close failures, including `RuntimeException`. The method 
reference is needed because `HoodieFileBinaryCopier` itself does not implement 
`AutoCloseable`.



##########
hudi-hadoop-common/src/main/java/org/apache/hudi/io/storage/hadoop/HoodieAvroHFileWriter.java:
##########
@@ -105,8 +106,14 @@ public HoodieAvroHFileWriter(String instantTime, 
StoragePath file, HoodieHFileCo
         .build();
     StorageConfiguration<Configuration> storageConf = new 
HadoopStorageConfiguration(conf);
     StoragePath filePath = new StoragePath(this.file.toUri());
-    OutputStream outputStream =  HoodieStorageUtils.getStorage(filePath, 
storageConf).create(filePath);
-    this.writer = new HFileWriterImpl(context, outputStream);
+    OutputStream outputStream = HoodieStorageUtils.getStorage(filePath, 
storageConf).create(filePath);
+    try {
+      this.writer = new HFileWriterImpl(context, outputStream);
+    } finally {
+      if (this.writer == null) {
+        closeQuietly(outputStream);

Review Comment:
   Fixed: the protected constructor region now includes `appendFileInfo`. On 
failure it closes the initialized writer, or the output stream if writer 
construction did not complete, and suppresses any cleanup failure onto the 
original exception. Added `testConstructorClosesWriterWhenFileInfoFails`, 
covering both successful cleanup and a failing close.



##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieBinaryCopyHandle.java:
##########
@@ -118,14 +118,26 @@ public void write() {
       log.info("Schema evolution enabled for binary copy: {}", 
schemaEvolutionEnabled);
       records = this.writer.binaryCopy(inputFiles, 
Collections.singletonList(path), writeScheMessageType, schemaEvolutionEnabled);
     } catch (IOException e) {
+      closeWriterAfterFailure(e);
       throw new HoodieIOException(e.getMessage(), e);
+    } catch (RuntimeException e) {
+      closeWriterAfterFailure(e);
+      throw e;
     } finally {
       this.recordsWritten = records;
       this.insertRecordsWritten = records;
     }
     log.info("Finish rewriting " + this.path + ". Using " + timer.endTimer() + 
" mills");
   }
 
+  private void closeWriterAfterFailure(Throwable failure) {
+    try {

Review Comment:
   Done. Binary-copy cleanup now shares `CloseableUtils.closeSuppressing` with 
the other handles. The duplicate `AutoCloseableUtils` class has been removed in 
favor of the utility already present on upstream master.



##########
hudi-hadoop-common/src/main/java/org/apache/hudi/parquet/io/HoodieParquetBinaryCopyBase.java:
##########
@@ -147,17 +154,42 @@ protected void initFileWriter(Path outPutFile, 
CompressionCodecName newCodecName
 
   @Override
   public void close() throws IOException {
+    if (writer == null) {
+      return;
+    }
     Map<String, String> extraMetaData = finalizeMetadata();
     extraMetaData = extraMetaData == null ? new HashMap<>() : extraMetaData;
     extraMetaData.remove("parquet.avro.schema");
     extraMetaData.remove("org.apache.spark.sql.parquet.row.metadata");
-    writer.end(extraMetaData);
-    // Release the buffer
-    reusableBlockBuffer = null;
+    try {
+      writer.end(extraMetaData);
+    } finally {
+      writer = null;
+      // Release the buffer
+      reusableBlockBuffer = null;
+    }
   }
 
   protected abstract Map<String, String> finalizeMetadata();
 
+  private void closeParquetFileWriterQuietly(ParquetFileWriter 
parquetFileWriter) {
+    if (parquetFileWriter == null) {
+      return;
+    }
+    Method closeMethod;
+    try {
+      // ParquetFileWriter does not implement Closeable in all versions, so 
attempt close() reflectively.
+      closeMethod = parquetFileWriter.getClass().getMethod("close");
+    } catch (NoSuchMethodException e) {
+      return;

Review Comment:
   Simplified this to an `instanceof AutoCloseable` check and removed 
reflection. The comment now identifies Parquet 1.12.x/1.13.x as lacking 
`close()`; newer versions implement `AutoCloseable`. The older-version 
failure-cleanup limitation is also called out in the PR description: fully 
fixing it requires direct output-stream ownership.



##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/HoodieWriteMergeHandle.java:
##########
@@ -485,10 +483,30 @@ public List<WriteStatus> close() {
 
       return Collections.singletonList(writeStatus);
     } catch (IOException e) {
+      closeFileWriterQuietly(e);
       throw new HoodieUpsertException("Failed to close UpdateHandle", e);
+    } catch (RuntimeException e) {
+      closeFileWriterQuietly(e);
+      throw e;
+    } finally {
+      keyToNewRecords = null;
+      writtenRecordKeys = null;

Review Comment:
   Done: removed the success-path assignments and kept `keyToNewRecords = null` 
and `writtenRecordKeys = null` in `finally`.



##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/io/BaseCreateHandle.java:
##########
@@ -168,6 +170,11 @@ protected HoodieRecord<T> updateFileName(HoodieRecord<T> 
record, HoodieSchema sc
     return record.prependMetaFields(schema, targetSchema, metadataValues, 
prop);
   }
 
+  private void closeFileWriterQuietly(Throwable failure) {

Review Comment:
   This was an oversight. Failure cleanup now calls `markClosed()` before 
closing the file writer. The create-handle regression test also checks 
`isClosed()` and verifies that a subsequent `close()` succeeds without 
attempting finalization again.



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