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

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


The following commit(s) were added to refs/heads/master by this push:
     new 0b6c88fac4 [core] Custom Partition expire factory should be invoked 
first (#6641)
0b6c88fac4 is described below

commit 0b6c88fac4cc4cc16a9f4c2996f0603615bb62bc
Author: Jingsong Lee <[email protected]>
AuthorDate: Thu Nov 20 17:52:51 2025 +0800

    [core] Custom Partition expire factory should be invoked first (#6641)
---
 .../shortcodes/generated/core_configuration.html   |  6 +--
 .../main/java/org/apache/paimon/CoreOptions.java   | 50 ++++++----------------
 .../org/apache/paimon/factories/FactoryUtil.java   | 11 +++--
 .../paimon/partition/PartitionExpireStrategy.java  | 24 +++++++----
 .../partition/PartitionExpireStrategyFactory.java  |  4 +-
 .../PartitionValuesTimeExpireStrategy.java         | 12 +++---
 .../CustomPartitionExpirationFactory.java          |  4 ++
 7 files changed, 49 insertions(+), 62 deletions(-)

diff --git a/docs/layouts/shortcodes/generated/core_configuration.html 
b/docs/layouts/shortcodes/generated/core_configuration.html
index c3251aa979..169b2b5027 100644
--- a/docs/layouts/shortcodes/generated/core_configuration.html
+++ b/docs/layouts/shortcodes/generated/core_configuration.html
@@ -895,9 +895,9 @@ This config option does not affect the default filesystem 
metastore.</td>
         </tr>
         <tr>
             <td><h5>partition.expiration-strategy</h5></td>
-            <td style="word-wrap: break-word;">values-time</td>
-            <td><p>Enum</p></td>
-            <td>The strategy determines how to extract the partition time and 
compare it with the current time.<br /><br />Possible 
values:<ul><li>"values-time": This strategy compares the time extracted from 
the partition value with the current time.</li><li>"update-time": This strategy 
compares the last update time of the partition with the current 
time.</li><li>"custom": This strategy use custom class to expire 
partitions.</li></ul></td>
+            <td style="word-wrap: break-word;">"values-time"</td>
+            <td>String</td>
+            <td>The strategy determines how to extract the partition time and 
compare it with the current time.<ul><li>"values-time": This strategy compares 
the time extracted from the partition value with the current 
time.</li><li>"update-time": This strategy compares the last update time of the 
partition with the current time.</li></ul></td>
         </tr>
         <tr>
             <td><h5>partition.expiration-time</h5></td>
diff --git a/paimon-api/src/main/java/org/apache/paimon/CoreOptions.java 
b/paimon-api/src/main/java/org/apache/paimon/CoreOptions.java
index ccb490ec41..648abc0bc6 100644
--- a/paimon-api/src/main/java/org/apache/paimon/CoreOptions.java
+++ b/paimon-api/src/main/java/org/apache/paimon/CoreOptions.java
@@ -1007,12 +1007,20 @@ public class CoreOptions implements Serializable {
                             "Whether only overwrite dynamic partition when 
overwriting a partitioned table with "
                                     + "dynamic partition columns. Works only 
when the table has partition keys.");
 
-    public static final ConfigOption<PartitionExpireStrategy> 
PARTITION_EXPIRATION_STRATEGY =
+    public static final ConfigOption<String> PARTITION_EXPIRATION_STRATEGY =
             key("partition.expiration-strategy")
-                    .enumType(PartitionExpireStrategy.class)
-                    .defaultValue(PartitionExpireStrategy.VALUES_TIME)
+                    .stringType()
+                    .defaultValue("values-time")
                     .withDescription(
-                            "The strategy determines how to extract the 
partition time and compare it with the current time.");
+                            Description.builder()
+                                    .text(
+                                            "The strategy determines how to 
extract the partition time and compare it with the current time.")
+                                    .list(
+                                            text(
+                                                    "\"values-time\": This 
strategy compares the time extracted from the partition value with the current 
time."),
+                                            text(
+                                                    "\"update-time\": This 
strategy compares the last update time of the partition with the current 
time."))
+                                    .build());
 
     public static final ConfigOption<Duration> PARTITION_EXPIRATION_TIME =
             key("partition.expiration-time")
@@ -2848,7 +2856,7 @@ public class CoreOptions implements Serializable {
                 .orElse(options.get(PARTITION_EXPIRATION_MAX_NUM));
     }
 
-    public PartitionExpireStrategy partitionExpireStrategy() {
+    public String partitionExpireStrategy() {
         return options.get(PARTITION_EXPIRATION_STRATEGY);
     }
 
@@ -3802,38 +3810,6 @@ public class CoreOptions implements Serializable {
         }
     }
 
-    /** Specifies the expiration strategy for partition expiration. */
-    public enum PartitionExpireStrategy implements DescribedEnum {
-        VALUES_TIME(
-                "values-time",
-                "This strategy compares the time extracted from the partition 
value with the current time."),
-
-        UPDATE_TIME(
-                "update-time",
-                "This strategy compares the last update time of the partition 
with the current time."),
-
-        CUSTOM("custom", "This strategy use custom class to expire 
partitions.");
-
-        private final String value;
-
-        private final String description;
-
-        PartitionExpireStrategy(String value, String description) {
-            this.value = value;
-            this.description = description;
-        }
-
-        @Override
-        public String toString() {
-            return value;
-        }
-
-        @Override
-        public InlineElement getDescription() {
-            return text(description);
-        }
-    }
-
     /** Specifies the strategy for selecting external storage paths. */
     public enum ExternalPathStrategy implements DescribedEnum {
         NONE(
diff --git 
a/paimon-api/src/main/java/org/apache/paimon/factories/FactoryUtil.java 
b/paimon-api/src/main/java/org/apache/paimon/factories/FactoryUtil.java
index 8431c77872..3293a1caee 100644
--- a/paimon-api/src/main/java/org/apache/paimon/factories/FactoryUtil.java
+++ b/paimon-api/src/main/java/org/apache/paimon/factories/FactoryUtil.java
@@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Optional;
 import java.util.ServiceLoader;
 import java.util.stream.Collectors;
 
@@ -149,13 +150,11 @@ public class FactoryUtil {
      * @param <T> the type of the factory
      * @return the factory
      */
-    public static <T> T discoverSingletonFactory(ClassLoader classLoader, 
Class<T> klass) {
+    public static <T> Optional<T> discoverSingletonFactory(
+            ClassLoader classLoader, Class<T> klass) {
         List<T> factories = FactoryUtil.discoverFactories(classLoader, klass);
         if (factories.isEmpty()) {
-            throw new FactoryException(
-                    String.format(
-                            "Could not find any factories that implement '%s' 
in the classpath.",
-                            klass.getName()));
+            return Optional.empty();
         }
 
         if (factories.size() > 1) {
@@ -171,6 +170,6 @@ public class FactoryUtil {
                                     .collect(Collectors.joining("\n"))));
         }
 
-        return factories.get(0);
+        return Optional.of(factories.get(0));
     }
 }
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategy.java
 
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategy.java
index ce021eb1d3..0921a65697 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategy.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategy.java
@@ -34,6 +34,7 @@ import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 /** Strategy for partition expiration. */
 public abstract class PartitionExpireStrategy {
@@ -80,18 +81,23 @@ public abstract class PartitionExpireStrategy {
             RowType partitionType,
             @Nullable CatalogLoader catalogLoader,
             @Nullable Identifier identifier) {
-        switch (options.partitionExpireStrategy()) {
-            case UPDATE_TIME:
+        Optional<PartitionExpireStrategyFactory> custom =
+                PartitionExpireStrategyFactory.INSTANCE.get();
+        if (custom.isPresent()) {
+            try {
+                return custom.get().create(catalogLoader, identifier, options, 
partitionType);
+            } catch (UnsupportedOperationException ignored) {
+            }
+        }
+
+        String strategy = options.partitionExpireStrategy();
+        switch (strategy) {
+            case "update-time":
                 return new PartitionUpdateTimeExpireStrategy(options, 
partitionType);
-            case VALUES_TIME:
+            case "values-time":
                 return new PartitionValuesTimeExpireStrategy(options, 
partitionType);
-            case CUSTOM:
-                return PartitionExpireStrategyFactory.INSTANCE
-                        .get()
-                        .create(catalogLoader, identifier, options, 
partitionType);
             default:
-                throw new IllegalArgumentException(
-                        "Unknown partitionExpireStrategy: " + 
options.partitionExpireStrategy());
+                throw new IllegalArgumentException("Unknown 
partitionExpireStrategy: " + strategy);
         }
     }
 }
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategyFactory.java
 
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategyFactory.java
index d871e4cfd2..192937b02b 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategyFactory.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionExpireStrategyFactory.java
@@ -27,6 +27,8 @@ import org.apache.paimon.types.RowType;
 import org.apache.paimon.shade.guava30.com.google.common.base.Supplier;
 import org.apache.paimon.shade.guava30.com.google.common.base.Suppliers;
 
+import java.util.Optional;
+
 /** Factory to create a {@link PartitionExpireStrategy}. */
 public interface PartitionExpireStrategyFactory {
 
@@ -36,7 +38,7 @@ public interface PartitionExpireStrategyFactory {
             CoreOptions options,
             RowType partitionType);
 
-    Supplier<PartitionExpireStrategyFactory> INSTANCE =
+    Supplier<Optional<PartitionExpireStrategyFactory>> INSTANCE =
             Suppliers.memoize(
                     () ->
                             FactoryUtil.discoverSingletonFactory(
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionValuesTimeExpireStrategy.java
 
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionValuesTimeExpireStrategy.java
index 70c55cfb38..6685a1d28c 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/partition/PartitionValuesTimeExpireStrategy.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/partition/PartitionValuesTimeExpireStrategy.java
@@ -36,6 +36,8 @@ import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+import static org.apache.paimon.CoreOptions.PARTITION_EXPIRATION_STRATEGY;
+
 /**
  * A partition expiration policy that compare the time extracted from the 
partition with the current
  * time.
@@ -86,19 +88,17 @@ public class PartitionValuesTimeExpireStrategy extends 
PartitionExpireStrategy {
                                 + "  1. Check the expiration configuration.\n"
                                 + "  2. Manually delete the partition using 
the drop-partition command if the partition"
                                 + " value is non-date formatted.\n"
-                                + "  3. Use '{}' expiration strategy by set 
'{}', which supports non-date formatted partition.",
+                                + "  3. Use 'update-time' expiration strategy 
by set '{}', which supports non-date formatted partition.",
                         formatPartitionInfo(array),
-                        CoreOptions.PartitionExpireStrategy.UPDATE_TIME,
-                        CoreOptions.PARTITION_EXPIRATION_STRATEGY.key());
+                        PARTITION_EXPIRATION_STRATEGY.key());
                 return false;
             } catch (NullPointerException e) {
                 // there might exist NULL partition value
                 LOG.warn(
                         "This partition {} cannot be expired because it 
contains null value. "
-                                + "You can try to drop it manually or use '{}' 
expiration strategy by set '{}'.",
+                                + "You can try to drop it manually or use 
'update-time' expiration strategy by set '{}'.",
                         formatPartitionInfo(array),
-                        CoreOptions.PartitionExpireStrategy.UPDATE_TIME,
-                        CoreOptions.PARTITION_EXPIRATION_STRATEGY.key());
+                        PARTITION_EXPIRATION_STRATEGY.key());
                 return false;
             }
         }
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/partition/CustomPartitionExpirationFactory.java
 
b/paimon-core/src/test/java/org/apache/paimon/partition/CustomPartitionExpirationFactory.java
index 9fa6369f93..601eba27b1 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/partition/CustomPartitionExpirationFactory.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/partition/CustomPartitionExpirationFactory.java
@@ -43,6 +43,10 @@ public class CustomPartitionExpirationFactory implements 
PartitionExpireStrategy
             Identifier identifier,
             CoreOptions options,
             RowType partitionType) {
+        String strategy = options.partitionExpireStrategy();
+        if (!"custom".equals(strategy)) {
+            throw new UnsupportedOperationException();
+        }
         return new PartitionExpireStrategy(partitionType, 
options.partitionDefaultName()) {
             @Override
             public List<PartitionEntry> selectExpiredPartitions(

Reply via email to