Dennis-Mircea Ciupitu created FLINK-40389:
---------------------------------------------

             Summary: Source parallelism is not capped at the partition count
                 Key: FLINK-40389
                 URL: https://issues.apache.org/jira/browse/FLINK-40389
             Project: Flink
          Issue Type: Bug
          Components: Autoscaler, Kubernetes Operator
            Reporter: Dennis-Mircea Ciupitu
             Fix For: kubernetes-operator-1.16.0


h1. Summary

A source vertex can be scaled far beyond its partition count. Every subtask 
above that count is assigned no split and stays idle, together with anything 
chained onto the source, so the slots are paid for and do no work.

Two separate defects combine. FLIP-586 (FLINK-39938) removed the cap for the 
new alignment modes, and the legacy modes never corrected the case where the 
current parallelism is already above the partition count, because of the 
direction guard added in FLINK-39299.
h1. Reproduction

Kafka source with 15 partitions, current parallelism 100, operator max 
parallelism 180, {{vertex.max-parallelism}} at its default of 200. Values are 
the result of {{{}JobVertexScaler#scale{}}}:
||raw target||new default (BALANCED)||legacy default (EVENLY_SPREAD)||correct||
|180 (scale up)|180|100 (scale blocked)|15|
|50 (scale down)|50|15|15|
|10 (scale down)|15|15|15|

Only the third row, where the target already sits at or below the partition 
count, is handled correctly by either.
h1. Cause
h2. New modes: the cap bounds the search, not the result

{{ParallelismAligner#firstAlignedInRegion}} scans {{[target, regionEnd]}} where 
{{regionEnd}} is derived from {{{}upperBoundForAlignment = min(N, 
maxParallelism, parallelismUpperLimit){}}}. When the target is already above 
that region the loop body never executes and the search returns 0. The built-in 
modes then keep the unaligned target:
{code:java}
int aligned = ParallelismAligner.firstAlignedInRegion(ctx, acceptLoadReducing);
return aligned > 0 ? aligned : ctx.getNewParallelism();
{code}
So the region is capped at 15 while the returned value is not.
h2. Legacy modes: the direction guard blocks a legitimate correction

The legacy modes fall back to {{{}relaxedDownwardFallback{}}}, which correctly 
arrives at 15, then pass it through {{{}applyBlockingFallback{}}}, whose guard 
is:
{code:java}
return (isScaleUp(ctx) && candidate <= ctx.getCurrentParallelism())
        || (!isScaleUp(ctx) && candidate >= ctx.getCurrentParallelism());
{code}
FLINK-39299 added this to stop the search drifting a few steps past 
{{currentParallelism}} and silently inverting a scale. It cannot distinguish 
that from the case here, where the candidate is legitimately far below the 
current parallelism because the partition count is a hard ceiling. With current 
100 and a scale-up requested, candidate 15 looks like an inversion, so the 
scale is blocked and the vertex stays at 100.
h2. Why only sources

For a keyed vertex the alignment target is the key group count, which is 
{{{}maxParallelism{}}}, and {{JobVertexScaler#scale}} already clamps to 
{{{}min(maxParallelism, parallelismUpperLimit){}}}. The target therefore can 
never exceed the ceiling and the defect cannot occur.

A source reports {{numSourcePartitions}} separately from 
{{{}maxParallelism{}}}, and nothing clamps the target to it, which is why 
sources are the only affected case.
h1. Proposed fix

Two small changes, both inside the alignment package, one per defect.

*1. Do not treat the alignment cap as a direction inversion* 
({{{}ParallelismAligner#invertsDirection{}}}). Landing exactly on the cap is 
the key group or partition count being the real limit rather than the search 
drifting, so correcting down to it is the intended outcome even when a scale-up 
was requested:
{code:java}
if (candidate == upperBoundForAlignment(ctx)) {
    return false;
}
{code}
*2. Never keep a target above the cap* 
({{{}BuiltInAlignmentMode#alignOrKeepTarget{}}}). When the region yields 
nothing, keep the computed target but bound it by the cap:
{code:java}
return Math.min(ctx.getNewParallelism(), 
ParallelismAligner.upperBoundForAlignment(ctx));
{code}
Verified against the reproduction: every row returns 15 for both the legacy and 
the new modes, the FLINK-39299 scale-up inversion case still blocks at 22, and 
the full {{flink-autoscaler}} suite passes unchanged (257 tests).

{{OFF}} overrides {{alignParallelism}} directly and is untouched, so explicitly 
disabling alignment still uses the computed target as-is.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to