FrankChen021 commented on code in PR #19687:
URL: https://github.com/apache/druid/pull/19687#discussion_r3703839007


##########
indexing-service/src/main/java/org/apache/druid/indexing/overlord/supervisor/SupervisorResource.java:
##########
@@ -534,6 +537,33 @@ public Response terminateAll(@Context final 
HttpServletRequest req)
     );
   }
 
+  @POST
+  @Path("/{id}/autoscaler")
+  @Consumes(MediaType.APPLICATION_JSON)
+  @Produces(MediaType.APPLICATION_JSON)
+  @ResourceFilters(SupervisorResourceFilter.class)

Review Comment:
   [P2] Authorize the read-only simulation as READ
   
   SupervisorResourceFilter derives authorization from the HTTP method, so this 
POST requires DATASOURCE WRITE permission. The endpoint only computes and 
returns a plot, while the new tab is exposed to users who can view Kafka 
supervisors; read-only operators therefore see the tab but every simulation 
request is rejected. Use a READ-specific authorization path or filter for this 
read-only POST.



##########
indexing-service/src/main/java/org/apache/druid/indexing/overlord/supervisor/SupervisorManager.java:
##########
@@ -644,6 +651,91 @@ public boolean isAnotherTaskGroupPublishingToPartitions(
     }
   }
 
+  /**
+   * Simulates the effects of the {@code costBased} auto-scaler by computing 
the optimal
+   * task count under various values of aggregate lag.
+   */
+  public Map<String, Object> simulateAutoscaling(
+      String supervisorId,
+      CostBasedAutoScalerConfig config,
+      int criticalLag,
+      int maxProcessingRatePerTask,
+      @Nullable Integer requestedTaskCount
+  )
+  {
+    // Validate that this is a streaming supervisor
+    final StreamSupervisor supervisor = requireStreamSupervisor(supervisorId, 
"simulateAutoscaling");
+
+    // Validate the inputs
+    InvalidInput.conditionalException(
+        criticalLag >= 1000,
+        "Value of critical lag[%d] must be 1000 or more",
+        criticalLag
+    );
+    InvalidInput.conditionalException(
+        maxProcessingRatePerTask >= 100,
+        "Value of maxProcessingRatePerTask[%d] must be 100 events per second 
or more",
+        maxProcessingRatePerTask
+    );
+    InvalidInput.conditionalException(
+        config.getTaskCountMin() >= 1,
+        "Value of taskCountMin[%d] must be 1 or more",
+        config.getTaskCountMin()
+    );
+    InvalidInput.conditionalException(
+        config.getTaskCountMax() <= MAX_SIMULATION_TASK_COUNT,
+        "Value of taskCountMax[%d] must be [%d] or less",
+        config.getTaskCountMax(), MAX_SIMULATION_TASK_COUNT
+    );
+
+    // Simulate from the supervisor's live task count unless the caller pins 
one.
+    final int currentTaskCount = Configs.valueOrDefault(
+        requestedTaskCount,
+        ((SeekableStreamSupervisor<?, ?, ?>) 
supervisor).getIoConfig().getTaskCount()
+    );
+    InvalidInput.conditionalException(
+        requestedTaskCount == null
+        || (currentTaskCount >= config.getTaskCountMin() && currentTaskCount 
<= config.getTaskCountMax()),
+        "Value of currentTaskCount[%d] must be within taskCountMin[%d] and 
taskCountMax[%d]",
+        currentTaskCount, config.getTaskCountMin(), config.getTaskCountMax()
+    );
+    final int simulationTaskCount = Math.max(
+        config.getTaskCountMin(),
+        Math.min(currentTaskCount, config.getTaskCountMax())
+    );
+
+    // Assumption: enough partitions to reach taskCountMax.
+    final int partitionCount = config.getTaskCountMax();
+    final int taskDurationSeconds = 3600;
+
+    // Assume that the tasks are fully used since there is some lag
+    final double avgProcessingRatePerTask = maxProcessingRatePerTask;
+    final double idleRatio = config.getOptimalTaskIdleRatio();
+
+    // Invoke the cost function for a variety of input values of lag
+    final Object[] rows = new Object[40];
+    final int lagStepSize = criticalLag / 20;
+    final CostBasedAutoScaler autoscaleSimulator = 
CostBasedAutoScaler.createSimulator(config, supervisorId);
+    for (int i = 0; i < 40; ++i) {
+      final double observedAggregateLag = (double) lagStepSize * i;
+      final CostMetrics costMetrics = new CostMetrics(
+          observedAggregateLag / partitionCount,
+          observedAggregateLag,
+          simulationTaskCount,
+          partitionCount,
+          idleRatio,
+          taskDurationSeconds,
+          avgProcessingRatePerTask,
+          maxProcessingRatePerTask * 1.0
+      );
+      final int optimalTaskCount = 
autoscaleSimulator.computeOptimalTaskCountInternal(costMetrics, true);

Review Comment:
   [P2] Suppress threshold logs during simulation
   
   Passing true suppresses the main computation log and metrics, but 
computeOptimalTaskCountInternal still emits INFO messages for every high- or 
critical-lag point. With the 40-row sweep and the submitted threshold, a single 
request emits roughly 25 INFO lines, and the UI issues another request after 
each debounced input change. Guard the high/critical threshold logs with 
simulation mode as well.



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