wombatu-kun commented on code in PR #20409:
URL: https://github.com/apache/hudi/pull/20409#discussion_r4355637189


##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/timeline/TimelineArchiverV1.java:
##########
@@ -150,9 +152,24 @@ public int archiveIfRequired(HoodieEngineContext context, 
boolean acquireLock) t
       addArchivalCommitMetrics(instantsToArchive);
       boolean success = true;
       if (!instantsToArchive.isEmpty()) {
-        this.writer = openWriter(archiveFilePath.getParent());
         log.info("Archiving instants {} for table {}", instantsToArchive, 
config.getBasePath());
-        archive(context, instantsToArchive);
+        try {
+          this.writer = openWriter(archiveFilePath.getParent());
+          archive(context, instantsToArchive);
+          close();
+        } catch (Exception e) {
+          try {
+            close();
+          } catch (Exception closeException) {
+            e.addSuppressed(closeException);
+          }
+          if (this.config.isFailOnTimelineArchivingEnabled()) {
+            throw e;
+          }
+          recordArchivalFailure(e);
+          log.warn("Failed to archive instants for table {}; active timeline 
files will not be deleted", config.getBasePath(), e);
+          return 0;

Review Comment:
   With the flag false, a V1 instant whose file is non-empty but unparseable 
now fails this batch on every attempt, so archival never gets past it and the 
active timeline grows without bound, with only a WARN and `archivalStatus=-1` 
to show for it. Is that the intended trade-off, or should a permanently 
unparseable instant still have a way through, e.g. archived as a placeholder 
the way an empty instant already is?



##########
hudi-client/hudi-client-common/src/test/java/org/apache/hudi/common/table/timeline/TestArchivedTimelineV2.java:
##########
@@ -135,10 +139,53 @@ void getInstantReaderReferencesSelf() {
     HoodieArchivedTimeline timeline = 
TIMELINE_FACTORY.createArchivedTimeline(metaClient);
     assertSame(timeline, timeline.getInstantReader());
   }
+
+  @Test
+  void testFailedActionDoesNotPublishPartialArchiveFile() throws Exception {
+    ActiveAction firstAction = createActiveAction("10000001");
+    ActiveAction failedAction = new DummyActiveAction(
+        INSTANT_GENERATOR.createNewInstant(HoodieInstant.State.COMPLETED, 
HoodieTimeline.COMMIT_ACTION, "10000002", "10000102"),
+        new byte[0]) {
+      @Override
+      public Option<byte[]> getCommitMetadata(HoodieTableMetaClient 
metaClient) {
+        throw new IllegalStateException("injected archive failure");
+      }
+    };
+    ActiveAction lastAction = createActiveAction("10000003");
+    LSMTimelineWriter writer = createTimelineWriter();
+
+    assertThrows(HoodieCommitException.class,
+        () -> writer.write(Arrays.asList(firstAction, failedAction, 
lastAction), Option.empty()));
+    assertTrue(metaClient.getArchivedTimeline().empty());
+    assertEquals(1, LSMTimeline.listAllMetaFiles(metaClient, 
metaClient.getArchivePath()).size());
+
+    writer.write(Arrays.asList(firstAction, createActiveAction("10000002"), 
lastAction), Option.empty());
+    assertEquals(3, metaClient.getArchivedTimeline().reload().countInstants());
+  }
+
+  @Test
+  void testCommittedArchiveFileMustContainAllExpectedActions() {
+    ActiveAction firstAction = createActiveAction("10000001");
+    ActiveAction middleAction = createActiveAction("10000002");
+    ActiveAction lastAction = createActiveAction("10000003");
+    LSMTimelineWriter writer = createTimelineWriter();
+
+    writer.write(Arrays.asList(firstAction, lastAction), Option.empty());

Review Comment:
   Only the rejecting branch of `containsAllActions` is covered, so a check 
that always returned false would still pass here while failing every legitimate 
retry of a committed batch. Could this test also re-write `[firstAction, 
lastAction]` right after this line and assert it returns without throwing?



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