Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion pkg/reconciler/autoscaling/kpa/kpa.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,12 @@ func computeActiveCondition(ctx context.Context, pa *autoscalingv1alpha1.PodAuto
// activeThreshold returns the scale required for the pa to be marked Active
func activeThreshold(ctx context.Context, pa *autoscalingv1alpha1.PodAutoscaler) int {
asConfig := config.FromContext(ctx).Autoscaler
min, _ := pa.ScaleBounds(asConfig)
min, maxScale := pa.ScaleBounds(asConfig)
if !pa.Status.IsScaleTargetInitialized() {
initialScale := resources.GetInitialScale(asConfig, pa)
if maxScale > 0 && initialScale > maxScale {
initialScale = maxScale
}
return int(max(min, initialScale))
}
return int(max(min, 1))
Expand Down
62 changes: 62 additions & 0 deletions pkg/reconciler/autoscaling/kpa/kpa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,59 @@ func TestReconcile(t *testing.T) {
Name: deployName,
Patch: fmt.Appendf(nil, `[{"op":"add","path":"/spec/replicas","value":%d}]`, 20),
}},
}, {
Name: "initial scale > maxScale, have not reached maxScale ready pods, PA still activating",
Key: key,
Ctx: context.WithValue(context.Background(), deciderKey{},
decider(testNamespace, testRevision, defaultScale, /* desiredScale */
-42 /* ebc */)),
Objects: append([]runtime.Object{
kpa(testNamespace, testRevision, WithPASKSReady, WithBufferedTraffic,
withScales(1, 1), WithReachabilityReachable,
withInitialScale(20), withMaxScale(2), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithSKSReady, WithNumActivators(3)),
defaultMetric, defaultDeployment,
}, makeReadyPods(1, testNamespace, testRevision)...),
WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
Object: kpa(testNamespace, testRevision, WithPASKSReady, WithBufferedTraffic,
withScales(1, 2), WithReachabilityReachable,
withInitialScale(20), withMaxScale(2), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
WithObservedGeneration(1),
),
}},
WantPatches: []clientgotesting.PatchActionImpl{{
ActionImpl: clientgotesting.ActionImpl{Namespace: testNamespace},
Name: deployName,
Patch: fmt.Appendf(nil, `[{"op":"add","path":"/spec/replicas","value":%d}]`, 2),
}},
}, {
Name: "initial scale > maxScale, reaching maxScale ready pods marks PA as active",
Key: key,
Ctx: context.WithValue(context.Background(), deciderKey{},
decider(testNamespace, testRevision, defaultScale, /* desiredScale */
-42 /* ebc */)),
Objects: append([]runtime.Object{
kpa(testNamespace, testRevision, WithPASKSReady, WithBufferedTraffic,
withScales(2, 2), withInitialScale(20), withMaxScale(2), WithReachabilityReachable,
WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
),
sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithSKSReady, WithNumActivators(3)),
defaultMetric,
deploy(testNamespace, testRevision, func(d *appsv1.Deployment) {
d.Spec.Replicas = ptr.Int32(defaultScale)
}),
}, makeReadyPods(2, testNamespace, testRevision)...),
WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
Object: kpa(testNamespace, testRevision, WithPASKSReady, WithTraffic,
markScaleTargetInitialized, withScales(2, 2), withInitialScale(20), withMaxScale(2), WithReachabilityReachable,
WithPAStatusService(testRevision), WithPAMetricsService(privateSvc), WithObservedGeneration(1),
),
}},
WantPatches: []clientgotesting.PatchActionImpl{{
ActionImpl: clientgotesting.ActionImpl{Namespace: testNamespace},
Name: deployName,
Patch: fmt.Appendf(nil, `[{"op":"add","path":"/spec/replicas","value":%d}]`, 2),
}},
}, {
Name: "initial scale zero: scale to zero",
Key: key,
Expand Down Expand Up @@ -2012,6 +2065,15 @@ func withInitialScale(initScale int) PodAutoscalerOption {
}
}

func withMaxScale(maxScale int) PodAutoscalerOption {
return func(pa *autoscalingv1alpha1.PodAutoscaler) {
pa.Annotations = kmap.Union(
pa.Annotations,
map[string]string{autoscaling.MaxScaleAnnotationKey: strconv.Itoa(maxScale)},
)
}
}

func TestComputeActivatorNum(t *testing.T) {
cases := []struct {
name string
Expand Down
3 changes: 3 additions & 0 deletions pkg/reconciler/autoscaling/kpa/scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,9 @@ func (ks *scaler) scale(ctx context.Context, pa *autoscalingv1alpha1.PodAutoscal
// Also ignore initialScale if the revision is unreachable (routingState = "reserve"),
// allowing it to scale down to 0 immediately.
if initialScale > 1 && !pa.Status.IsScaleTargetInitialized() && pa.Spec.Reachability != autoscalingv1alpha1.ReachabilityUnreachable {
if maxScale > 0 && initialScale > maxScale {
initialScale = maxScale
}
// Ignore initial scale if minScale >= initialScale.
if minScale < initialScale {
logger.Debugf("Adjusting minScale to meet the initial scale: %d -> %d", minScale, initialScale)
Expand Down
83 changes: 83 additions & 0 deletions pkg/reconciler/autoscaling/kpa/scaler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,17 @@ func TestScaler(t *testing.T) {
paMarkActivating(k, time.Now())
k.Annotations[autoscaling.InitialScaleAnnotationKey] = "5"
},
}, {
label: "initial scale does not exceed maxScale",
startReplicas: 0,
scaleTo: 1,
maxScale: 2,
wantReplicas: 2,
wantScaling: true,
paMutation: func(k *autoscalingv1alpha1.PodAutoscaler) {
paMarkActivating(k, time.Now())
k.Annotations[autoscaling.InitialScaleAnnotationKey] = "5"
},
}, {
label: "reaching initial scale zero",
startReplicas: 0,
Expand All @@ -504,6 +515,52 @@ func TestScaler(t *testing.T) {
configMutator: func(c *config.Config) {
c.Autoscaler.AllowZeroInitialScale = true
},
}, {
label: "initial scale with maxScale = 0 (unlimited)",
startReplicas: 0,
scaleTo: 1,
maxScale: 0,
wantReplicas: 5,
wantScaling: true,
paMutation: func(k *autoscalingv1alpha1.PodAutoscaler) {
paMarkActivating(k, time.Now())
k.Annotations[autoscaling.InitialScaleAnnotationKey] = "5"
},
}, {
label: "initial scale <= 1 respects desired scale within maxScale",
startReplicas: 0,
scaleTo: 1,
maxScale: 2,
wantReplicas: 1,
wantScaling: true,
paMutation: func(k *autoscalingv1alpha1.PodAutoscaler) {
paMarkActivating(k, time.Now())
k.Annotations[autoscaling.InitialScaleAnnotationKey] = "1"
},
}, {
label: "minScale < maxScale < initialScale clamps to maxScale",
startReplicas: 0,
scaleTo: 1,
minScale: 2,
maxScale: 4,
wantReplicas: 4,
wantScaling: true,
paMutation: func(k *autoscalingv1alpha1.PodAutoscaler) {
paMarkActivating(k, time.Now())
k.Annotations[autoscaling.InitialScaleAnnotationKey] = "6"
},
}, {
label: "initialScale < minScale < maxScale respects minScale",
startReplicas: 0,
scaleTo: 1,
minScale: 3,
maxScale: 5,
wantReplicas: 3,
wantScaling: true,
paMutation: func(k *autoscalingv1alpha1.PodAutoscaler) {
paMarkActivating(k, time.Now())
k.Annotations[autoscaling.InitialScaleAnnotationKey] = "2"
},
}, {
label: "unreachable revision with initialScale > 1 can scale to 0",
startReplicas: 2,
Expand All @@ -517,6 +574,32 @@ func TestScaler(t *testing.T) {
// Mark as unreachable (routingState = "reserve")
k.Spec.Reachability = autoscalingv1alpha1.ReachabilityUnreachable
},
}, {
label: "unreachable revision with initialScale > maxScale can scale to 0",
startReplicas: 2,
scaleTo: 0,
maxScale: 2,
wantReplicas: 1, // First we deactivate and scale to 1.
wantScaling: true,
wantCBCount: 1,
paMutation: func(k *autoscalingv1alpha1.PodAutoscaler) {
paMarkActive(k, time.Now().Add(-2*time.Minute))
k.Annotations[autoscaling.InitialScaleAnnotationKey] = "5"
// Mark as unreachable (routingState = "reserve")
k.Spec.Reachability = autoscalingv1alpha1.ReachabilityUnreachable
},
}, {
label: "already initialized scale target ignores initialScale exceeding maxScale",
startReplicas: 2,
scaleTo: 1,
maxScale: 2,
wantReplicas: 1,
wantScaling: true,
paMutation: func(k *autoscalingv1alpha1.PodAutoscaler) {
paMarkActive(k, time.Now().Add(-2*time.Minute))
k.Status.MarkScaleTargetInitialized()
k.Annotations[autoscaling.InitialScaleAnnotationKey] = "5"
},
}}

for _, test := range tests {
Expand Down
Loading