kfaraz commented on code in PR #19622:
URL: https://github.com/apache/druid/pull/19622#discussion_r3472792643


##########
indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/CostBasedAutoScaler.java:
##########
@@ -103,6 +115,11 @@ public CostBasedAutoScaler(
     this.costFunction = new WeightedCostFunction();
     this.autoscalerExecutor = 
Execs.scheduledSingleThreaded("CostBasedAutoScaler-"
                                                             + 
StringUtils.encodeForFormat(spec.getId()));
+
+    // Bound the rate watermark to roughly one task's lifetime (in 
collectMetrics() ticks).
+    final long taskDurationMillis = 
supervisor.getIoConfig().getTaskDuration().getStandardSeconds() * 1000L;
+    this.lagGatedRateWindowSize = Math.max(1, (int) (taskDurationMillis / 
config.getScaleActionPeriodMillis()));

Review Comment:
   So, if task duration is 1 hour and scaling period is 15 mins, the window 
size would be only 4?
   I feel like we should be looking at a much larger window if we are trying to 
determine the max, maybe even 100?
   It probably doesn't have to be a function of the task duration, just gating 
the sample collection on the lag condition should be enough.



##########
indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/CostBasedAutoScalerConfig.java:
##########
@@ -91,7 +92,8 @@ public CostBasedAutoScalerConfig(
       @Nullable @JsonProperty("useTaskCountBoundariesOnScaleDown") Boolean 
useTaskCountBoundariesOnScaleDown,
       @Nullable @JsonProperty("minScaleUpDelay") Duration minScaleUpDelay,
       @Nullable @JsonProperty("minScaleDownDelay") Duration minScaleDownDelay,
-      @Nullable @JsonProperty("scaleDownDuringTaskRolloverOnly") Boolean 
scaleDownDuringTaskRolloverOnly
+      @Nullable @JsonProperty("scaleDownDuringTaskRolloverOnly") Boolean 
scaleDownDuringTaskRolloverOnly,
+      @Nullable @JsonProperty("useUtilizationRatio") Boolean 
useUtilizationRatio

Review Comment:
   We should invert the meaning of this flag and name it `usePollIdleRatio` 
(true by default) since that is a concept already being used in this 
auto-scaler. Adding another new terminology (utilization ratio) makes things 
more confusing.



##########
indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/CostBasedAutoScaler.java:
##########
@@ -85,6 +87,16 @@ public class CostBasedAutoScaler implements 
SupervisorTaskAutoScaler
   private final CostBasedAutoScalerConfig config;
   private final ScheduledExecutorService autoscalerExecutor;
   private final WeightedCostFunction costFunction;
+  /**
+   * Recent {@code avgProcessingRate} readings, but only from ticks where lag 
was present.
+   * "Lag-gated" because lag is the condition that decides whether a reading 
counts as evidence
+   * of capacity at all: with no lag the task is only processing as fast as 
data arrives (an
+   * upstream supply number, not a capacity number); with lag present, demand 
isn't the
+   * bottleneck, so the rate observed is the task's own ceiling. The max of 
these samples
+   * becomes {@link CostMetrics#getMaxObservedRate()}.
+   */
+  private final EvictingQueue<Double> lagGatedRateSamples;

Review Comment:
   ```suggestion
     private final EvictingQueue<Double> lagGatedProcessingRateSamples;
   ```



##########
indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/CostBasedAutoScaler.java:
##########
@@ -103,6 +115,11 @@ public CostBasedAutoScaler(
     this.costFunction = new WeightedCostFunction();
     this.autoscalerExecutor = 
Execs.scheduledSingleThreaded("CostBasedAutoScaler-"
                                                             + 
StringUtils.encodeForFormat(spec.getId()));
+
+    // Bound the rate watermark to roughly one task's lifetime (in 
collectMetrics() ticks).
+    final long taskDurationMillis = 
supervisor.getIoConfig().getTaskDuration().getStandardSeconds() * 1000L;

Review Comment:
   If task duration is specified as less than 1s (e.g. in embedded tests), will 
this become 0? I think we should use `Duration.toMillis()` to be on the 
safe-side. Please double check this either way.



##########
embedded-tests/src/test/java/org/apache/druid/testing/embedded/indexing/autoscaler/CostBasedAutoScalerIntegrationTest.java:
##########
@@ -337,7 +398,9 @@ private int getCurrentTaskCount(String supervisorId)
     final String getSupervisorPath = 
StringUtils.format("/druid/indexer/v1/supervisor/%s", supervisorId);
     final KafkaSupervisorSpec supervisorSpec = 
cluster.callApi().serviceClient().onLeaderOverlord(
         mapper -> new RequestBuilder(HttpMethod.GET, getSupervisorPath),
-        new TypeReference<>(){}
+        new TypeReference<>()
+        {
+        }

Review Comment:
   Nit: extra change



##########
indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/CostBasedAutoScaler.java:
##########
@@ -85,6 +87,16 @@ public class CostBasedAutoScaler implements 
SupervisorTaskAutoScaler
   private final CostBasedAutoScalerConfig config;
   private final ScheduledExecutorService autoscalerExecutor;
   private final WeightedCostFunction costFunction;
+  /**
+   * Recent {@code avgProcessingRate} readings, but only from ticks where lag 
was present.
+   * "Lag-gated" because lag is the condition that decides whether a reading 
counts as evidence
+   * of capacity at all: with no lag the task is only processing as fast as 
data arrives (an
+   * upstream supply number, not a capacity number); with lag present, demand 
isn't the
+   * bottleneck, so the rate observed is the task's own ceiling. The max of 
these samples
+   * becomes {@link CostMetrics#getMaxObservedRate()}.

Review Comment:
   This can be simplified:
   
   ```suggestion
      * When there is no lag i.e. tasks are able to keep up with incoming data, 
the processing rate
      * is dictated by the rate of incoming data alone and does not denote the 
actual processing
      * capacity of the task.
   ```



##########
indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/CostBasedAutoScalerConfig.java:
##########
@@ -282,6 +285,15 @@ public boolean isScaleDownOnTaskRolloverOnly()
     return scaleDownDuringTaskRolloverOnly;
   }
 
+  /**
+   * Opt-in flag for replacing default idle-ratio with the rate-based 
utilization idle ratio.
+   */
+  @JsonProperty("useUtilizationRatio")
+  public boolean shouldUseUtilizationRatio()

Review Comment:
   Use the prefix `is` for boolean configs to align with the rest of the druid 
code. You can also skip the String literal inside the `@JsonProperty` 
annotation.
   
   ```suggestion
     public boolean isUseUtilizationRatio()
   ```



##########
indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/WeightedCostFunction.java:
##########
@@ -156,6 +157,19 @@ public CostResult computeCost(
     return new CostResult(cost, lagCost, weightedIdleCost);
   }
 
+  /**
+   * Derives the current idle ratio from measured utilization ({@code 
avgProcessingRate / maxObservedRate}).
+   */
+  static double estimateIdleFromUtilization(CostMetrics metrics)

Review Comment:
   Maybe this method should exist in `CostMetrics` itself?



##########
indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/WeightedCostFunction.java:
##########
@@ -156,6 +157,19 @@ public CostResult computeCost(
     return new CostResult(cost, lagCost, weightedIdleCost);
   }
 
+  /**
+   * Derives the current idle ratio from measured utilization ({@code 
avgProcessingRate / maxObservedRate}).
+   */
+  static double estimateIdleFromUtilization(CostMetrics metrics)
+  {
+    final Double maxObservedRate = metrics.getMaxObservedRate();
+    if (maxObservedRate == null || maxObservedRate <= 0) {
+      return IDEAL_IDLE_RATIO;

Review Comment:
   So, if there has been no lag for a while, we will assume that we are already 
in the optimal usage band?
   This would mean that scale down would not happen when, ideally, it should.
   
   I wonder if it would be better to collect samples of `avgProcessingRate` 
even when there is no lag, and simply use the max to determine the max observed 
rate.



##########
indexing-service/src/main/java/org/apache/druid/indexing/seekablestream/supervisor/autoscaler/CostBasedAutoScaler.java:
##########
@@ -85,6 +87,16 @@ public class CostBasedAutoScaler implements 
SupervisorTaskAutoScaler
   private final CostBasedAutoScalerConfig config;
   private final ScheduledExecutorService autoscalerExecutor;
   private final WeightedCostFunction costFunction;
+  /**
+   * Recent {@code avgProcessingRate} readings, but only from ticks where lag 
was present.
+   * "Lag-gated" because lag is the condition that decides whether a reading 
counts as evidence
+   * of capacity at all: with no lag the task is only processing as fast as 
data arrives (an
+   * upstream supply number, not a capacity number); with lag present, demand 
isn't the
+   * bottleneck, so the rate observed is the task's own ceiling. The max of 
these samples
+   * becomes {@link CostMetrics#getMaxObservedRate()}.
+   */
+  private final EvictingQueue<Double> lagGatedRateSamples;
+  private final int lagGatedRateWindowSize;

Review Comment:
   This probably doesn't need to be a member field of this class.



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


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to