@@ -185,9 +185,9 @@ public ParallelismChange computeScaleTargetParallelism(
185185 LOG .debug ("Target processing capacity for {} is {}" , vertex , targetCapacity );
186186 double scaleFactor = targetCapacity / averageTrueProcessingRate ;
187187 if (conf .get (OBSERVED_SCALABILITY_ENABLED )) {
188+
188189 double scalingCoefficient =
189- JobVertexScaler .calculateObservedScalingCoefficient (
190- history , conf .get (OBSERVED_SCALABILITY_MIN_OBSERVATIONS ));
190+ JobVertexScaler .calculateObservedScalingCoefficient (history , conf );
191191 scaleFactor = scaleFactor / scalingCoefficient ;
192192 }
193193 double minScaleFactor = 1 - conf .get (MAX_SCALE_DOWN_FACTOR );
@@ -251,22 +251,19 @@ public ParallelismChange computeScaleTargetParallelism(
251251 /**
252252 * Calculates the scaling coefficient based on historical scaling data.
253253 *
254- * <p>The scaling coefficient is computed using a weighted least squares approach, where more
255- * recent data points and those with higher parallelism are given higher weights. If there are
256- * not enough observations, or if the computed coefficient is invalid, a default value of {@code
254+ * <p>The scaling coefficient is computed using the least squares approach. If there are not
255+ * enough observations, or if the computed coefficient is invalid, a default value of {@code
257256 * 1.0} is returned, assuming linear scaling.
258257 *
259258 * @param history A {@code SortedMap} of {@code Instant} timestamps to {@code ScalingSummary}
260- * @param minObservations The minimum number of observations required to compute the scaling
261- * coefficient. If the number of historical entries is less than this threshold, a default
262- * coefficient of {@code 1.0} is returned.
259+ * @param conf Deployment configuration.
263260 * @return The computed scaling coefficient.
264261 */
265262 @ VisibleForTesting
266263 protected static double calculateObservedScalingCoefficient (
267- SortedMap <Instant , ScalingSummary > history , int minObservations ) {
264+ SortedMap <Instant , ScalingSummary > history , Configuration conf ) {
268265 /*
269- * The scaling coefficient is computed using a **weighted least squares** approach
266+ * The scaling coefficient is computed using the least squares approach
270267 * to fit a linear model:
271268 *
272269 * R_i = β * P_i * α
@@ -277,18 +274,21 @@ protected static double calculateObservedScalingCoefficient(
277274 * - β = baseline processing rate
278275 * - α = scaling coefficient to optimize
279276 *
280- * The optimization minimizes the **weighted sum of squared errors**:
277+ * The optimization minimizes the **sum of squared errors**:
281278 *
282- * Loss = ∑ w_i * (R_i - β * α * P_i)^2
279+ * Loss = ∑ (R_i - β * α * P_i)^2
283280 *
284281 * Differentiating w.r.t. α and solving for α:
285282 *
286- * α = ∑ (w_i * P_i * R_i) / (∑ (w_i * P_i^2) * β)
283+ * α = ∑ (P_i * R_i) / (∑ (P_i^2) * β)
287284 *
288- * We keep the system conservative for higher returns scenario by clamping computed α within 1.0.
285+ * We keep the system conservative for higher returns scenario by clamping computed α to an upper bound of 1.0.
286+ * If the computed coefficient falls below threshold, the system falls back to assuming linear scaling (α = 1.0).
289287 */
290288
291- // not enough data to compute scaling coefficient. we assume linear scaling.
289+ var minObservations = conf .get (OBSERVED_SCALABILITY_MIN_OBSERVATIONS );
290+
291+ // not enough data to compute scaling coefficient; we assume linear scaling.
292292 if (history .isEmpty () || history .size () < minObservations ) {
293293 return 1.0 ;
294294 }
@@ -299,14 +299,10 @@ protected static double calculateObservedScalingCoefficient(
299299 return 1.0 ;
300300 }
301301
302- Instant latestTimestamp = history .lastKey ();
303-
304302 List <Double > parallelismList = new ArrayList <>();
305303 List <Double > processingRateList = new ArrayList <>();
306- List <Double > weightList = new ArrayList <>();
307304
308305 for (Map .Entry <Instant , ScalingSummary > entry : history .entrySet ()) {
309- Instant timestamp = entry .getKey ();
310306 ScalingSummary summary = entry .getValue ();
311307 double parallelism = summary .getCurrentParallelism ();
312308 double processingRate = summary .getMetrics ().get (TRUE_PROCESSING_RATE ).getAverage ();
@@ -317,25 +313,24 @@ protected static double calculateObservedScalingCoefficient(
317313 return 1.0 ;
318314 }
319315
320- // Compute weight based on recency & parallelism
321- double timeDiff =
322- Duration .between (timestamp , latestTimestamp ).getSeconds ()
323- + 1 ; // Avoid division by zero
324- double weight = parallelism / timeDiff ;
325-
326316 parallelismList .add (parallelism );
327317 processingRateList .add (processingRate );
328- weightList .add (weight );
329318 }
330319
331320 var coefficient =
332321 AutoScalerUtils .optimizeLinearScalingCoefficient (
333- parallelismList ,
334- processingRateList ,
335- weightList ,
336- baselineProcessingRate ,
337- 1 ,
338- 0.01 );
322+ parallelismList , processingRateList , baselineProcessingRate , 1 , 0.01 );
323+
324+ double threshold =
325+ conf .get (AutoScalerOptions .SCALING_EFFECTIVENESS_DETECTION_ENABLED )
326+ ? conf .get (AutoScalerOptions .SCALING_EFFECTIVENESS_THRESHOLD )
327+ : 0.5 ;
328+
329+ if (coefficient < threshold ) {
330+ LOG .warn ("Scaling coefficient is below threshold. Falling back to linear scaling." );
331+ return 1.0 ;
332+ }
333+
339334 return BigDecimal .valueOf (coefficient ).setScale (2 , RoundingMode .CEILING ).doubleValue ();
340335 }
341336
0 commit comments