This is an automated email from the ASF dual-hosted git repository.

danny0405 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hudi.git


The following commit(s) were added to refs/heads/master by this push:
     new 1ec3e338e437 [MINOR] NullPointerException when reading action state of 
old archived timelines (#13579)
1ec3e338e437 is described below

commit 1ec3e338e4377c37ff6854eecf3ba22e0992055c
Author: Tim Brown <[email protected]>
AuthorDate: Sat Jul 19 06:15:46 2025 -0400

    [MINOR] NullPointerException when reading action state of old archived 
timelines (#13579)
---
 .../timeline/versioning/v1/ArchivedTimelineV1.java |   3 ++-
 .../table/timeline/TestArchivedTimelineV1.java     |  15 +++++++++++++
 .../.hoodie/archived/.commits_.archive.1_1-0-1     | Bin 0 -> 15136 bytes
 .../archived_timeline/.hoodie/hoodie.properties    |  24 +++++++++++++++++++++
 4 files changed, 41 insertions(+), 1 deletion(-)

diff --git 
a/hudi-common/src/main/java/org/apache/hudi/common/table/timeline/versioning/v1/ArchivedTimelineV1.java
 
b/hudi-common/src/main/java/org/apache/hudi/common/table/timeline/versioning/v1/ArchivedTimelineV1.java
index 0d4d4d624149..7b1101e6f68d 100644
--- 
a/hudi-common/src/main/java/org/apache/hudi/common/table/timeline/versioning/v1/ArchivedTimelineV1.java
+++ 
b/hudi-common/src/main/java/org/apache/hudi/common/table/timeline/versioning/v1/ArchivedTimelineV1.java
@@ -271,7 +271,8 @@ public class ArchivedTimelineV1 extends BaseTimelineV1 
implements HoodieArchived
                                            TimeRangeFilter timeRangeFilter) {
     final String action = record.get(ACTION_TYPE_KEY).toString();
     final String stateTransitionTime = (String) 
record.get(STATE_TRANSITION_TIME);
-    final HoodieInstant hoodieInstant = new 
HoodieInstant(HoodieInstant.State.valueOf(record.get(ACTION_STATE).toString()), 
action,
+    final HoodieInstant.State actionState = 
Option.ofNullable(record.get(ACTION_STATE)).map(state -> 
HoodieInstant.State.valueOf(state.toString())).orElse(HoodieInstant.State.COMPLETED);
+    final HoodieInstant hoodieInstant = new HoodieInstant(actionState, action,
         instantTime, stateTransitionTime, 
InstantComparatorV1.REQUESTED_TIME_BASED_COMPARATOR);
     if (timeRangeFilter != null && 
!timeRangeFilter.isInRange(hoodieInstant.requestedTime())) {
       return Option.empty();
diff --git 
a/hudi-hadoop-common/src/test/java/org/apache/hudi/common/table/timeline/TestArchivedTimelineV1.java
 
b/hudi-hadoop-common/src/test/java/org/apache/hudi/common/table/timeline/TestArchivedTimelineV1.java
index be4112ee9dec..69cd03daf378 100644
--- 
a/hudi-hadoop-common/src/test/java/org/apache/hudi/common/table/timeline/TestArchivedTimelineV1.java
+++ 
b/hudi-hadoop-common/src/test/java/org/apache/hudi/common/table/timeline/TestArchivedTimelineV1.java
@@ -35,6 +35,7 @@ import org.apache.hudi.common.model.HoodieCommitMetadata;
 import org.apache.hudi.common.model.HoodieRecord;
 import org.apache.hudi.common.model.HoodieRollingStatMetadata;
 import org.apache.hudi.common.model.WriteOperationType;
+import org.apache.hudi.common.table.HoodieTableMetaClient;
 import org.apache.hudi.common.table.log.HoodieLogFormat;
 import org.apache.hudi.common.table.log.HoodieLogFormat.Writer;
 import org.apache.hudi.common.table.log.block.HoodieAvroDataBlock;
@@ -71,6 +72,7 @@ import java.util.stream.Stream;
 import static 
org.apache.hudi.common.table.timeline.HoodieInstant.State.COMPLETED;
 import static 
org.apache.hudi.common.table.timeline.HoodieInstant.State.INFLIGHT;
 import static 
org.apache.hudi.common.table.timeline.HoodieInstant.State.REQUESTED;
+import static 
org.apache.hudi.common.testutils.HoodieTestUtils.getDefaultStorageConf;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -419,6 +421,19 @@ public class TestArchivedTimelineV1 extends 
HoodieCommonTestHarness {
     assertEquals(instants, timeline.getInstants());
   }
 
+  @Test
+  void readLegacyArchivedTimelineWithNullFields() {
+    String samplePath = 
this.getClass().getClassLoader().getResource("archived_timeline").getPath();
+    metaClient = HoodieTableMetaClient.builder()
+        .setBasePath(samplePath)
+        .setConf(getDefaultStorageConf())
+        .setLoadActiveTimelineOnLoad(false)
+        .build();
+    HoodieArchivedTimeline timeline = new ArchivedTimelineV1(metaClient);
+    timeline.loadCompletedInstantDetailsInMemory();
+    assertEquals(3, timeline.getInstants().size());
+  }
+
   /**
    * Validate whether the instants of given timestamps of the hudi archived 
timeline are loaded to memory or not.
    * @param hoodieArchivedTimeline archived timeline to test against
diff --git 
a/hudi-hadoop-common/src/test/resources/archived_timeline/.hoodie/archived/.commits_.archive.1_1-0-1
 
b/hudi-hadoop-common/src/test/resources/archived_timeline/.hoodie/archived/.commits_.archive.1_1-0-1
new file mode 100644
index 000000000000..3485b0ff5941
Binary files /dev/null and 
b/hudi-hadoop-common/src/test/resources/archived_timeline/.hoodie/archived/.commits_.archive.1_1-0-1
 differ
diff --git 
a/hudi-hadoop-common/src/test/resources/archived_timeline/.hoodie/hoodie.properties
 
b/hudi-hadoop-common/src/test/resources/archived_timeline/.hoodie/hoodie.properties
new file mode 100644
index 000000000000..9d3ecdb5d407
--- /dev/null
+++ 
b/hudi-hadoop-common/src/test/resources/archived_timeline/.hoodie/hoodie.properties
@@ -0,0 +1,24 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+hoodie.table.name=raw_trips
+hoodie.archivelog.folder=archived
+hoodie.table.type=COPY_ON_WRITE
+hoodie.table.version=6
+hoodie.table.partition.fields=some_nonexistent_field
+hoodie.timeline.layout.version=1
+hoodie.datasource.write.drop.partition.columns=false
+hoodie.table.checksum=3360473891

Reply via email to