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

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


The following commit(s) were added to refs/heads/master by this push:
     new cba670fa4f Fallback for bad values in periodic controller task 
configurations (#15466)
cba670fa4f is described below

commit cba670fa4fcd9a82fb6155563b9ea25ff0ad668b
Author: ayesheepatra07 <ayeshee.pa...@startree.ai>
AuthorDate: Tue May 6 16:13:53 2025 -0700

    Fallback for bad values in periodic controller task configurations (#15466)
---
 .../apache/pinot/controller/ControllerConf.java    | 62 ++++++++++++++++++++--
 .../api/resources/PinotControllerHealthCheck.java  | 16 ++++++
 .../pinot/controller/ControllerConfTest.java       | 22 ++++++--
 3 files changed, 92 insertions(+), 8 deletions(-)

diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerConf.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerConf.java
index d405c4dcf0..55728b0fc9 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerConf.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerConf.java
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import org.apache.commons.configuration2.Configuration;
 import org.apache.commons.lang3.StringUtils;
@@ -376,6 +377,8 @@ public class ControllerConf extends PinotConfiguration {
   public static final String EXIT_ON_SCHEMA_CHECK_FAILURE = 
"controller.startup.exitOnSchemaCheckFailure";
   public static final boolean DEFAULT_EXIT_ON_SCHEMA_CHECK_FAILURE = true;
 
+  private final Map<String, String> _invalidConfigs = new 
ConcurrentHashMap<>();
+
   public ControllerConf() {
     super(new HashMap<>());
   }
@@ -602,6 +605,8 @@ public class ControllerConf extends PinotConfiguration {
 
   public int getRetentionControllerFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.RETENTION_MANAGER_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            ControllerPeriodicTasksConf.RETENTION_MANAGER_FREQUENCY_PERIOD, 
period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
             () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_RETENTION_MANAGER_FREQUENCY_IN_SECONDS,
                 
ControllerPeriodicTasksConf.DEFAULT_RETENTION_MANAGER_FREQUENCY_IN_SECONDS));
@@ -625,6 +630,8 @@ public class ControllerConf extends PinotConfiguration {
   public int getOfflineSegmentIntervalCheckerFrequencyInSeconds() {
     return Optional.ofNullable(
             
getProperty(ControllerPeriodicTasksConf.OFFLINE_SEGMENT_INTERVAL_CHECKER_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            
ControllerPeriodicTasksConf.OFFLINE_SEGMENT_INTERVAL_CHECKER_FREQUENCY_PERIOD, 
period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(() -> 
getProperty(
             
ControllerPeriodicTasksConf.DEPRECATED_OFFLINE_SEGMENT_INTERVAL_CHECKER_FREQUENCY_IN_SECONDS,
             
ControllerPeriodicTasksConf.DEFAULT_OFFLINE_SEGMENT_INTERVAL_CHECKER_FREQUENCY_IN_SECONDS));
@@ -647,6 +654,8 @@ public class ControllerConf extends PinotConfiguration {
    */
   public int getRealtimeSegmentValidationFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.REALTIME_SEGMENT_VALIDATION_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            
ControllerPeriodicTasksConf.REALTIME_SEGMENT_VALIDATION_FREQUENCY_PERIOD, 
period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
             () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_REALTIME_SEGMENT_VALIDATION_FREQUENCY_IN_SECONDS,
                 
ControllerPeriodicTasksConf.DEFAULT_REALTIME_SEGMENT_VALIDATION_FREQUENCY_IN_SECONDS));
@@ -669,6 +678,8 @@ public class ControllerConf extends PinotConfiguration {
    */
   public int getBrokerResourceValidationFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.BROKER_RESOURCE_VALIDATION_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            
ControllerPeriodicTasksConf.BROKER_RESOURCE_VALIDATION_FREQUENCY_PERIOD, 
period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
             () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_BROKER_RESOURCE_VALIDATION_FREQUENCY_IN_SECONDS,
                 
ControllerPeriodicTasksConf.DEFAULT_BROKER_RESOURCE_VALIDATION_FREQUENCY_IN_SECONDS));
@@ -686,6 +697,7 @@ public class ControllerConf extends PinotConfiguration {
 
   public int getStatusCheckerFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.STATUS_CHECKER_FREQUENCY_PERIOD))
+        .filter(period -> 
isValidPeriodWithLogging(ControllerPeriodicTasksConf.STATUS_CHECKER_FREQUENCY_PERIOD,
 period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
             () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_STATUS_CHECKER_FREQUENCY_IN_SECONDS,
                 
ControllerPeriodicTasksConf.DEFAULT_STATUS_CHECKER_FREQUENCY_IN_SECONDS));
@@ -698,6 +710,8 @@ public class ControllerConf extends PinotConfiguration {
 
   public int getRebalanceCheckerFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.REBALANCE_CHECKER_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            ControllerPeriodicTasksConf.REBALANCE_CHECKER_FREQUENCY_PERIOD, 
period))
         .map(period -> (int) convertPeriodToSeconds(period))
         
.orElse(ControllerPeriodicTasksConf.DEFAULT_REBALANCE_CHECKER_FREQUENCY_IN_SECONDS);
   }
@@ -720,6 +734,8 @@ public class ControllerConf extends PinotConfiguration {
 
   public int getTaskMetricsEmitterFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.TASK_METRICS_EMITTER_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            ControllerPeriodicTasksConf.TASK_METRICS_EMITTER_FREQUENCY_PERIOD, 
period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
             () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_TASK_METRICS_EMITTER_FREQUENCY_IN_SECONDS,
                 
ControllerPeriodicTasksConf.DEFAULT_TASK_METRICS_EMITTER_FREQUENCY_IN_SECONDS));
@@ -732,6 +748,8 @@ public class ControllerConf extends PinotConfiguration {
 
   public int getStatusCheckerWaitForPushTimeInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.STATUS_CHECKER_WAIT_FOR_PUSH_TIME_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            
ControllerPeriodicTasksConf.STATUS_CHECKER_WAIT_FOR_PUSH_TIME_PERIOD, period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
             () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_STATUS_CHECKER_WAIT_FOR_PUSH_TIME_IN_SECONDS,
                 
ControllerPeriodicTasksConf.DEFAULT_STATUS_CONTROLLER_WAIT_FOR_PUSH_TIME_IN_SECONDS));
@@ -749,6 +767,8 @@ public class ControllerConf extends PinotConfiguration {
    */
   public int getSegmentRelocatorFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.SEGMENT_RELOCATOR_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            ControllerPeriodicTasksConf.SEGMENT_RELOCATOR_FREQUENCY_PERIOD, 
period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(() -> {
           Integer segmentRelocatorFreqSeconds =
               
getProperty(ControllerPeriodicTasksConf.DEPRECATED_SEGMENT_RELOCATOR_FREQUENCY_IN_SECONDS,
 Integer.class);
@@ -877,6 +897,8 @@ public class ControllerConf extends PinotConfiguration {
 
   public int getTaskManagerFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.TASK_MANAGER_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            ControllerPeriodicTasksConf.TASK_MANAGER_FREQUENCY_PERIOD, period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
             () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_TASK_MANAGER_FREQUENCY_IN_SECONDS,
                 
ControllerPeriodicTasksConf.DEFAULT_TASK_MANAGER_FREQUENCY_IN_SECONDS));
@@ -890,6 +912,8 @@ public class ControllerConf extends PinotConfiguration {
   @Deprecated
   public int getMinionInstancesCleanupTaskFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.MINION_INSTANCES_CLEANUP_TASK_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            
ControllerPeriodicTasksConf.MINION_INSTANCES_CLEANUP_TASK_FREQUENCY_PERIOD, 
period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
             () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_MINION_INSTANCES_CLEANUP_TASK_FREQUENCY_IN_SECONDS,
                 
ControllerPeriodicTasksConf.DEFAULT_MINION_INSTANCES_CLEANUP_TASK_FREQUENCY_IN_SECONDS));
@@ -917,6 +941,8 @@ public class ControllerConf extends PinotConfiguration {
   public int 
getMinionInstancesCleanupTaskMinOfflineTimeBeforeDeletionInSeconds() {
     return Optional.ofNullable(
         
getProperty(ControllerPeriodicTasksConf.MINION_INSTANCES_CLEANUP_TASK_MIN_OFFLINE_TIME_BEFORE_DELETION_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            
ControllerPeriodicTasksConf.MINION_INSTANCES_CLEANUP_TASK_MIN_OFFLINE_TIME_BEFORE_DELETION_PERIOD,
 period))
         .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(() -> 
getProperty(
             ControllerPeriodicTasksConf.
                 
DEPRECATED_MINION_INSTANCES_CLEANUP_TASK_MIN_OFFLINE_TIME_BEFORE_DELETION_SECONDS,
@@ -933,6 +959,8 @@ public class ControllerConf extends PinotConfiguration {
 
   public int getStaleInstancesCleanupTaskFrequencyInSeconds() {
     return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.STALE_INSTANCES_CLEANUP_TASK_FREQUENCY_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            
ControllerPeriodicTasksConf.STALE_INSTANCES_CLEANUP_TASK_FREQUENCY_PERIOD, 
period))
         .map(period -> (int) convertPeriodToSeconds(period))
         // Backward compatible for existing users who configured 
MinionInstancesCleanupTask
         .orElse(getMinionInstancesCleanupTaskFrequencyInSeconds());
@@ -955,6 +983,8 @@ public class ControllerConf extends PinotConfiguration {
   public int getStaleInstancesCleanupTaskInstancesRetentionInSeconds() {
     return Optional.ofNullable(
             
getProperty(ControllerPeriodicTasksConf.STALE_INSTANCES_CLEANUP_TASK_INSTANCES_RETENTION_PERIOD))
+        .filter(period -> isValidPeriodWithLogging(
+            
ControllerPeriodicTasksConf.STALE_INSTANCES_CLEANUP_TASK_INSTANCES_RETENTION_PERIOD,
 period))
         .map(period -> (int) convertPeriodToSeconds(period))
         // Backward compatible for existing users who configured 
MinionInstancesCleanupTask
         
.orElse(getMinionInstancesCleanupTaskMinOfflineTimeBeforeDeletionInSeconds());
@@ -1076,13 +1106,16 @@ public class ControllerConf extends PinotConfiguration {
     return getProperty(ENABLE_HYBRID_TABLE_RETENTION_STRATEGY, 
DEFAULT_ENABLE_HYBRID_TABLE_RETENTION_STRATEGY);
   }
 
-  public int getSegmentLevelValidationIntervalInSeconds() {
-    return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.SEGMENT_LEVEL_VALIDATION_INTERVAL_PERIOD))
-        .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
-            () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_SEGMENT_LEVEL_VALIDATION_INTERVAL_IN_SECONDS,
-                
ControllerPeriodicTasksConf.DEFAULT_SEGMENT_LEVEL_VALIDATION_INTERVAL_IN_SECONDS));
+   public int getSegmentLevelValidationIntervalInSeconds() {
+      return 
Optional.ofNullable(getProperty(ControllerPeriodicTasksConf.SEGMENT_LEVEL_VALIDATION_INTERVAL_PERIOD))
+          .filter(period -> isValidPeriodWithLogging(
+              
ControllerPeriodicTasksConf.SEGMENT_LEVEL_VALIDATION_INTERVAL_PERIOD, period))
+          .map(period -> (int) convertPeriodToSeconds(period)).orElseGet(
+              () -> 
getProperty(ControllerPeriodicTasksConf.DEPRECATED_SEGMENT_LEVEL_VALIDATION_INTERVAL_IN_SECONDS,
+                  
ControllerPeriodicTasksConf.DEFAULT_SEGMENT_LEVEL_VALIDATION_INTERVAL_IN_SECONDS));
   }
 
+
   public boolean isAutoResetErrorSegmentsOnValidationEnabled() {
     return 
getProperty(ControllerPeriodicTasksConf.AUTO_RESET_ERROR_SEGMENTS_VALIDATION, 
false);
   }
@@ -1226,6 +1259,25 @@ public class ControllerConf extends PinotConfiguration {
     return convertPeriodToUnit(period, TimeUnit.SECONDS);
   }
 
+  private boolean isValidPeriodWithLogging(String propertyKey, String 
periodStr) {
+    if (TimeUtils.isPeriodValid(periodStr)) {
+      return true;
+    } else {
+      addControllerInvalidConfigs(propertyKey,
+          String.format("Invalid time spec '%s' for config '%s'. Falling back 
to default config.",
+              periodStr, propertyKey));
+      return false;
+    }
+  }
+
+  private void addControllerInvalidConfigs(String propertyKey, String 
errorMessage) {
+    _invalidConfigs.put(propertyKey, errorMessage);
+  }
+
+  public Map<String, String> getInvalidConfigs() {
+    return _invalidConfigs;
+  }
+
   private String getSupportedProtocol(String property) {
     String value = getProperty(property, CommonConstants.HTTP_PROTOCOL);
     Preconditions.checkArgument(SUPPORTED_PROTOCOLS.contains(value), 
"Unsupported %s protocol '%s'", property, value);
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotControllerHealthCheck.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotControllerHealthCheck.java
index 61a18352ea..cf1de22181 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotControllerHealthCheck.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotControllerHealthCheck.java
@@ -29,6 +29,8 @@ import io.swagger.annotations.SwaggerDefinition;
 import java.time.Duration;
 import java.time.Instant;
 import java.time.format.DateTimeFormatter;
+import java.util.Collections;
+import java.util.Map;
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.ws.rs.GET;
@@ -119,6 +121,20 @@ public class PinotControllerHealthCheck {
     return uptime.getSeconds();
   }
 
+  @GET
+  @Path("invalidconfigs")
+  @Authorize(targetType = TargetType.CLUSTER, action = 
Actions.Cluster.GET_HEALTH)
+  @Produces(MediaType.APPLICATION_JSON)
+  @ApiOperation(value = "List invalid controller configs")
+  public Response getConfigWarnings() {
+    Map<String, String> warnings = _controllerConf.getInvalidConfigs();
+    if (warnings.isEmpty()) {
+      return Response.ok(Collections.emptyMap()).build();
+    }
+
+    return Response.ok(warnings).build();
+  }
+
   @GET
   @Path("start-time")
   @Authorize(targetType = TargetType.CLUSTER, action = 
Actions.Cluster.GET_HEALTH)
diff --git 
a/pinot-controller/src/test/java/org/apache/pinot/controller/ControllerConfTest.java
 
b/pinot-controller/src/test/java/org/apache/pinot/controller/ControllerConfTest.java
index c65a1b7a9f..120da8e8db 100644
--- 
a/pinot-controller/src/test/java/org/apache/pinot/controller/ControllerConfTest.java
+++ 
b/pinot-controller/src/test/java/org/apache/pinot/controller/ControllerConfTest.java
@@ -88,9 +88,10 @@ public class ControllerConfTest {
    * are thrown when invalid new configurations are read (there is no 
fall-back to the corresponding
    * valid deprecated configuration). For all valid new configurations, they 
override the
    * corresponding deprecated configuration.
+   * Added fallback logic to use valid deprecated config when new config is 
invalid.
    */
   @Test
-  public void 
invalidNewConfigShouldThrowExceptionOnReadWithoutFallbackToCorrespondingValidDeprecatedConfig()
 {
+  public void 
invalidNewConfigShouldNotThrowExceptionOnReadWithFallbackToCorrespondingValidDeprecatedConfig()
 {
     //setup
     Map<String, Object> controllerConfig = new HashMap<>();
     int durationInSeconds = getRandomDurationInSeconds();
@@ -99,9 +100,24 @@ public class ControllerConfTest {
     String randomPeriodInMinutes = getRandomPeriodInMinutes();
     NEW_CONFIGS.forEach(config -> controllerConfig.put(config, 
randomPeriodInMinutes));
     //put some invalid new configs
-    controllerConfig.put(RETENTION_MANAGER_FREQUENCY_PERIOD, 
getRandomString());
+    String randomInvalidString = getRandomString();
+    controllerConfig.put(RETENTION_MANAGER_FREQUENCY_PERIOD, 
randomInvalidString);
     ControllerConf conf = new ControllerConf(controllerConfig);
-    Assert.assertThrows(IllegalArgumentException.class, 
conf::getRetentionControllerFrequencyInSeconds);
+    Assert.assertEquals(
+        conf.getRetentionControllerFrequencyInSeconds(),
+        durationInSeconds, // expected fallback value
+        "Should fallback to deprecated config value"
+    );
+    //test to assert that invalid config is captured in the invalid config map 
value
+    Map<String, String> invalidConfigs = conf.getInvalidConfigs();
+    
Assert.assertTrue(invalidConfigs.containsKey(RETENTION_MANAGER_FREQUENCY_PERIOD));
+    Assert.assertEquals(
+        conf.getInvalidConfigs().get(RETENTION_MANAGER_FREQUENCY_PERIOD),
+        String.format(
+            "Invalid time spec '%s' for config '%s'. Falling back to default 
config.",
+            randomInvalidString, RETENTION_MANAGER_FREQUENCY_PERIOD
+        )
+    );
   }
 
   /**


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org
For additional commands, e-mail: commits-h...@pinot.apache.org

Reply via email to