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

wangxianghu 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 3da0a9807dd1 fix(partition-ttl): Fix IllegalArgumentException in 
KeepByTimeStrategy when no candidate partitions exist (#19092)
3da0a9807dd1 is described below

commit 3da0a9807dd11d038e73b6747de53ba57f979000
Author: wangxianghu <[email protected]>
AuthorDate: Tue Jun 30 20:17:03 2026 +0800

    fix(partition-ttl): Fix IllegalArgumentException in KeepByTimeStrategy when 
no candidate partitions exist (#19092)
    
    * fix(partition-ttl): Fix IllegalArgumentException in KeepByTimeStrategy 
when no candidate partitions exist
---
 .../action/ttl/strategy/KeepByTimeStrategy.java    |  4 ++
 .../ttl/strategy/TestKeepByTimeStrategy.java       | 62 ++++++++++++++++++++++
 2 files changed, 66 insertions(+)

diff --git 
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/action/ttl/strategy/KeepByTimeStrategy.java
 
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/action/ttl/strategy/KeepByTimeStrategy.java
index 329fee22cf60..8968d69f70bd 100644
--- 
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/action/ttl/strategy/KeepByTimeStrategy.java
+++ 
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/table/action/ttl/strategy/KeepByTimeStrategy.java
@@ -81,6 +81,10 @@ public class KeepByTimeStrategy extends PartitionTTLStrategy 
{
    * @param partitionPaths Partitions to collect stats.
    */
   private Map<String, Option<String>> 
getLastCommitTimeForPartitions(List<String> partitionPaths) {
+    if (partitionPaths.isEmpty()) {
+      log.info("Candidate partition paths list is empty, skip TTL stats 
collection");
+      return Collections.emptyMap();
+    }
     int statsParallelism = Math.min(partitionPaths.size(), 200);
     return hoodieTable.getContext().map(partitionPaths, partitionPath -> {
       Option<String> partitionLastModifiedTime = hoodieTable.getHoodieView()
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/table/action/ttl/strategy/TestKeepByTimeStrategy.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/table/action/ttl/strategy/TestKeepByTimeStrategy.java
new file mode 100644
index 000000000000..309eab2d6013
--- /dev/null
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/table/action/ttl/strategy/TestKeepByTimeStrategy.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package org.apache.hudi.table.action.ttl.strategy;
+
+import org.apache.hudi.config.HoodieWriteConfig;
+import org.apache.hudi.table.HoodieTable;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link KeepByTimeStrategy}.
+ */
+public class TestKeepByTimeStrategy {
+
+  /**
+   * Regression test: when there are no candidate partitions to evaluate,
+   * the strategy must short-circuit and return an empty result instead of
+   * handing a parallelism of 0 to the engine, which would surface as:
+   *   java.lang.IllegalArgumentException: Positive number of partitions 
required
+   * from ParallelCollectionRDD.slice on the Spark path.
+   */
+  @Test
+  public void 
testGetExpiredPartitionsForTimeStrategy_emptyInput_returnsEmptyWithoutTouchingEngine()
 {
+    HoodieTable hoodieTable = mock(HoodieTable.class);
+    HoodieWriteConfig writeConfig = mock(HoodieWriteConfig.class);
+    when(hoodieTable.getConfig()).thenReturn(writeConfig);
+    when(writeConfig.getPartitionTTLStrategyDaysRetain()).thenReturn(10);
+
+    KeepByTimeStrategy strategy = new KeepByTimeStrategy(hoodieTable, 
"20240101000000000");
+
+    List<String> expired = 
strategy.getExpiredPartitionsForTimeStrategy(Collections.emptyList());
+
+    assertTrue(expired.isEmpty(), "Empty candidate list should yield no 
expired partitions");
+    // Crucial: we must never reach the engine map call with parallelism=0.
+    verify(hoodieTable, never()).getContext();
+  }
+}

Reply via email to