Skip to content

[FLINK-39890] Fix NaN in observed scaling coefficient when baseline rate is zero#1134

Open
lrsb wants to merge 1 commit into
apache:mainfrom
lrsb:fix-autoscaler-observed-scaling-coefficient-nan
Open

[FLINK-39890] Fix NaN in observed scaling coefficient when baseline rate is zero#1134
lrsb wants to merge 1 commit into
apache:mainfrom
lrsb:fix-autoscaler-observed-scaling-coefficient-nan

Conversation

@lrsb

@lrsb lrsb commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

What is the purpose of the change

When job.autoscaler.observed-scalability.enabled is true, the autoscaler throws a NumberFormatException while computing the observed scaling coefficient for any vertex with a zero true-processing-rate (idle / very low traffic). The exception aborts the entire scaling pass, so no parallelism overrides / resource requirements are applied and the job stays stuck at its deployed parallelism.

The root cause is in AutoScalerUtils.optimizeLinearScalingCoefficient: the denominator is squaredSum * baselineProcessingRate, but only squaredSum is guarded against zero. An idle vertex reports a finite true processing rate of 0.0 (passing the caller's isNaN guard), giving baselineProcessingRate == 0.0 and sum == 0.0. The result is alpha = 0.0 / 0.0 = NaN, which Math.max/Math.min do not sanitize, so it reaches BigDecimal.valueOf(NaN) and fails.

This change guards the full denominator so a zero baseline (or zero squaredSum) falls back to linear scaling.

Brief change log

  • Guard the complete denominator squaredSum * baselineProcessingRate in AutoScalerUtils.optimizeLinearScalingCoefficient, returning the linear-scaling fallback (1.0) when it is zero

Verifying this change

This change added tests and can be verified as follows:

  • Added testCalculateScalingCoefficientWithZeroProcessingRateFallsBackToLinear to JobVertexScalerTest, which builds a scaling history of vertices with a zero true-processing-rate and asserts calculateObservedScalingCoefficient returns the linear-scaling coefficient 1.0 instead of producing NaN

Does this pull request potentially affect one of the following parts:

  • Dependencies (does it add or upgrade a dependency): no
  • The public API, i.e., is any changes to the CustomResourceDescriptors: no
  • Core observer or reconciler logic that is regularly executed: yes (the observed scaling coefficient is computed on every scaling pass)

Documentation

  • Does this pull request introduce a new feature? no
  • If yes, how is the feature documented? not applicable

@lrsb lrsb marked this pull request as ready for review June 9, 2026 09:31

@Dennis-Mircea Dennis-Mircea left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a real reproduction for this? From I checked, in a real case scenario the baselineProcessingRate will never reach this code as 0.

@lrsb

lrsb commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Yes, this is actually addressing prod issue we encountered. If you are asking for steps is can try to create a simple case.

@Dennis-Mircea

Copy link
Copy Markdown
Contributor

Yes, this is actually addressing prod issue we encountered. If you are asking for steps is can try to create a simple case.

Thanks, I believe you hit a real NumberFormatException in prod. My point is that the value triggering it is +Infinity, not 0.

An idle vertex doesn't report TRUE_PROCESSING_RATE = 0.0. In ScalingMetricEvaluator.computeTprFromBusyTime, rate == 0 returns Double.POSITIVE_INFINITY ("nothing coming in, assume infinite processing power"), and the observed-TPR path does the same. So baselineProcessingRate becomes +Infinity, not 0.

With that, your current guard doesn't catch it:

  • denominator = squaredSum * (+Infinity) = +Infinity, so denominator == 0.0 is false.
  • the loop only skips NaN, not Infinity, so sum becomes +Infinity too.
  • alpha = +Infinity / +Infinity = NaN, which Math.max/Math.min don't sanitize, so BigDecimal.valueOf(NaN) still throws.

So this patch would not fix the prod crash. The fix needs to handle non-finite values. Either guard the result (if (!Double.isFinite(alpha)) return 1.0;), or drop non-finite observations from the fit (!Double.isFinite(...) instead of isNaN(...) for both baselineProcessingRate and each processingRate).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants