Skip to content

Commit f9ffa7f

Browse files
committed
add additional log & UT for HPA
add additional log & UT for HPA
1 parent 0f2bde7 commit f9ffa7f

File tree

2 files changed

+118
-11
lines changed

2 files changed

+118
-11
lines changed

pkg/controller/podautoscaler/horizontal.go

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ import (
5858
var (
5959
scaleUpLimitFactor = 2.0
6060
scaleUpLimitMinimum = 4.0
61+
// recommendationOutputLimit is the maximum number of recommendations that will be logged.
62+
recommendationOutputLimit = 100
6163
)
6264

6365
var (
@@ -845,9 +847,11 @@ func (a *HorizontalController) reconcileAutoscaler(ctx context.Context, hpaShare
845847

846848
logger.V(4).Info("Proposing desired replicas",
847849
"desiredReplicas", metricDesiredReplicas,
850+
"currentReplicas", currentReplicas,
848851
"metric", metricName,
849852
"timestamp", metricTimestamp,
850-
"scaleTarget", reference)
853+
"scaleTarget", reference,
854+
"metrics", metricStatuses)
851855

852856
rescaleMetric := ""
853857
if metricDesiredReplicas > desiredReplicas {
@@ -861,9 +865,9 @@ func (a *HorizontalController) reconcileAutoscaler(ctx context.Context, hpaShare
861865
rescaleReason = "All metrics below target"
862866
}
863867
if hpa.Spec.Behavior == nil {
864-
desiredReplicas = a.normalizeDesiredReplicas(hpa, key, currentReplicas, desiredReplicas, minReplicas)
868+
desiredReplicas = a.normalizeDesiredReplicas(ctx, hpa, key, currentReplicas, desiredReplicas, minReplicas)
865869
} else {
866-
desiredReplicas = a.normalizeDesiredReplicasWithBehaviors(hpa, key, currentReplicas, desiredReplicas, minReplicas)
870+
desiredReplicas = a.normalizeDesiredReplicasWithBehaviors(ctx, hpa, key, currentReplicas, desiredReplicas, minReplicas)
867871
}
868872
rescale = desiredReplicas != currentReplicas
869873
}
@@ -885,6 +889,7 @@ func (a *HorizontalController) reconcileAutoscaler(ctx context.Context, hpaShare
885889
a.storeScaleEvent(hpa.Spec.Behavior, key, currentReplicas, desiredReplicas)
886890
logger.Info("Successfully rescaled",
887891
"HPA", klog.KObj(hpa),
892+
"scaleTarget", reference,
888893
"currentReplicas", currentReplicas,
889894
"desiredReplicas", desiredReplicas,
890895
"reason", rescaleReason)
@@ -942,7 +947,7 @@ func (a *HorizontalController) stabilizeRecommendation(key string, prenormalized
942947

943948
// normalizeDesiredReplicas takes the metrics desired replicas value and normalizes it based on the appropriate conditions (i.e. < maxReplicas, >
944949
// minReplicas, etc...)
945-
func (a *HorizontalController) normalizeDesiredReplicas(hpa *autoscalingv2.HorizontalPodAutoscaler, key string, currentReplicas int32, prenormalizedDesiredReplicas int32, minReplicas int32) int32 {
950+
func (a *HorizontalController) normalizeDesiredReplicas(ctx context.Context, hpa *autoscalingv2.HorizontalPodAutoscaler, key string, currentReplicas int32, prenormalizedDesiredReplicas int32, minReplicas int32) int32 {
946951
stabilizedRecommendation := a.stabilizeRecommendation(key, prenormalizedDesiredReplicas)
947952
if stabilizedRecommendation != prenormalizedDesiredReplicas {
948953
setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, "ScaleDownStabilized", "recent recommendations were higher than current one, applying the highest recent recommendation")
@@ -957,6 +962,16 @@ func (a *HorizontalController) normalizeDesiredReplicas(hpa *autoscalingv2.Horiz
957962
} else {
958963
setCondition(hpa, autoscalingv2.ScalingLimited, v1.ConditionTrue, reason, "%s", message)
959964
}
965+
logger := klog.FromContext(ctx)
966+
reference := fmt.Sprintf("%s/%s/%s", hpa.Spec.ScaleTargetRef.Kind, hpa.Namespace, hpa.Spec.ScaleTargetRef.Name)
967+
logger.V(4).Info("Normalized desired replicas",
968+
"scaleTarget", reference,
969+
"currentReplicas", currentReplicas,
970+
"desiredReplicas", desiredReplicas,
971+
"minReplicas", minReplicas,
972+
"stabilizedRecommendation", stabilizedRecommendation,
973+
"prenormalizedDesiredReplicas", prenormalizedDesiredReplicas,
974+
"reason", reason)
960975

961976
return desiredReplicas
962977
}
@@ -977,7 +992,7 @@ type NormalizationArg struct {
977992
// 2. Apply the scale up/down limits from the hpaSpec.Behaviors (i.e. add no more than 4 pods)
978993
// 3. Apply the constraints period (i.e. add no more than 4 pods per minute)
979994
// 4. Apply the stabilization (i.e. add no more than 4 pods per minute, and pick the smallest recommendation during last 5 minutes)
980-
func (a *HorizontalController) normalizeDesiredReplicasWithBehaviors(hpa *autoscalingv2.HorizontalPodAutoscaler, key string, currentReplicas, prenormalizedDesiredReplicas, minReplicas int32) int32 {
995+
func (a *HorizontalController) normalizeDesiredReplicasWithBehaviors(ctx context.Context, hpa *autoscalingv2.HorizontalPodAutoscaler, key string, currentReplicas, prenormalizedDesiredReplicas, minReplicas int32) int32 {
981996
a.maybeInitScaleDownStabilizationWindow(hpa)
982997
normalizationArg := NormalizationArg{
983998
Key: key,
@@ -987,21 +1002,42 @@ func (a *HorizontalController) normalizeDesiredReplicasWithBehaviors(hpa *autosc
9871002
MaxReplicas: hpa.Spec.MaxReplicas,
9881003
CurrentReplicas: currentReplicas,
9891004
DesiredReplicas: prenormalizedDesiredReplicas}
990-
stabilizedRecommendation, reason, message := a.stabilizeRecommendationWithBehaviors(normalizationArg)
1005+
stabilizedRecommendation, reason, message := a.stabilizeRecommendationWithBehaviors(ctx, normalizationArg)
9911006
normalizationArg.DesiredReplicas = stabilizedRecommendation
9921007
if stabilizedRecommendation != prenormalizedDesiredReplicas {
9931008
// "ScaleUpStabilized" || "ScaleDownStabilized"
9941009
setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, reason, "%s", message)
9951010
} else {
9961011
setCondition(hpa, autoscalingv2.AbleToScale, v1.ConditionTrue, "ReadyForNewScale", "recommended size matches current size")
9971012
}
998-
desiredReplicas, reason, message := a.convertDesiredReplicasWithBehaviorRate(normalizationArg)
1013+
1014+
logger := klog.FromContext(ctx)
1015+
reference := fmt.Sprintf("%s/%s/%s", hpa.Spec.ScaleTargetRef.Kind, hpa.Namespace, hpa.Spec.ScaleTargetRef.Name)
1016+
logger.V(4).Info("Normalized desired replicas with behaviors - after stabilized recommendation",
1017+
"scaleTarget", reference,
1018+
"currentReplicas", currentReplicas,
1019+
"minReplicas", minReplicas,
1020+
"stabilizedRecommendation", stabilizedRecommendation,
1021+
"prenormalizedDesiredReplicas", prenormalizedDesiredReplicas,
1022+
"reason", reason,
1023+
"message", message)
1024+
1025+
desiredReplicas, reason, message := a.convertDesiredReplicasWithBehaviorRate(ctx, normalizationArg)
9991026
if desiredReplicas == stabilizedRecommendation {
10001027
setCondition(hpa, autoscalingv2.ScalingLimited, v1.ConditionFalse, reason, "%s", message)
10011028
} else {
10021029
setCondition(hpa, autoscalingv2.ScalingLimited, v1.ConditionTrue, reason, "%s", message)
10031030
}
10041031

1032+
logger.V(4).Info("Normalized desired replicas with behaviors - after rated recommendation",
1033+
"scaleTarget", reference,
1034+
"currentReplicas", currentReplicas,
1035+
"minReplicas", minReplicas,
1036+
"stabilizedRecommendation", stabilizedRecommendation,
1037+
"desiredReplicas", desiredReplicas,
1038+
"reason", reason,
1039+
"message", message)
1040+
10051041
return desiredReplicas
10061042
}
10071043

@@ -1090,7 +1126,7 @@ func (a *HorizontalController) storeScaleEvent(behavior *autoscalingv2.Horizonta
10901126
// stabilizeRecommendationWithBehaviors:
10911127
// - replaces old recommendation with the newest recommendation,
10921128
// - returns {max,min} of recommendations that are not older than constraints.Scale{Up,Down}.DelaySeconds
1093-
func (a *HorizontalController) stabilizeRecommendationWithBehaviors(args NormalizationArg) (int32, string, string) {
1129+
func (a *HorizontalController) stabilizeRecommendationWithBehaviors(ctx context.Context, args NormalizationArg) (int32, string, string) {
10941130
now := time.Now()
10951131

10961132
foundOldSample := false
@@ -1128,6 +1164,24 @@ func (a *HorizontalController) stabilizeRecommendationWithBehaviors(args Normali
11281164
if recommendation > downRecommendation {
11291165
recommendation = downRecommendation
11301166
}
1167+
// Only keep the recommendations and ignore timestamp for logging, keep last recommendationOutputLimit recommendations
1168+
var recommendations = make([]int32, 0, recommendationOutputLimit)
1169+
start := len(a.recommendations[args.Key]) - recommendationOutputLimit
1170+
if start < 0 {
1171+
start = 0
1172+
}
1173+
for i := start; i < len(a.recommendations[args.Key]); i++ {
1174+
recommendations = append(recommendations, a.recommendations[args.Key][i].recommendation)
1175+
}
1176+
1177+
logger := klog.FromContext(ctx)
1178+
logger.V(4).Info("Stabilizing recommendation",
1179+
"key", args.Key,
1180+
"currentReplicas", args.CurrentReplicas,
1181+
"desiredReplicas", args.DesiredReplicas,
1182+
"upRecommendation", upRecommendation,
1183+
"downRecommendation", downRecommendation,
1184+
"recommendations", recommendations)
11311185

11321186
// Record the unstabilized recommendation.
11331187
if foundOldSample {
@@ -1150,14 +1204,22 @@ func (a *HorizontalController) stabilizeRecommendationWithBehaviors(args Normali
11501204

11511205
// convertDesiredReplicasWithBehaviorRate performs the actual normalization, given the constraint rate
11521206
// It doesn't consider the stabilizationWindow, it is done separately
1153-
func (a *HorizontalController) convertDesiredReplicasWithBehaviorRate(args NormalizationArg) (int32, string, string) {
1207+
func (a *HorizontalController) convertDesiredReplicasWithBehaviorRate(ctx context.Context, args NormalizationArg) (int32, string, string) {
11541208
var possibleLimitingReason, possibleLimitingMessage string
1209+
logger := klog.FromContext(ctx)
11551210

11561211
if args.DesiredReplicas > args.CurrentReplicas {
11571212
a.scaleUpEventsLock.RLock()
11581213
defer a.scaleUpEventsLock.RUnlock()
11591214
a.scaleDownEventsLock.RLock()
11601215
defer a.scaleDownEventsLock.RUnlock()
1216+
logger.V(4).Info("Converting desired replicas with behavior rate - scale up",
1217+
"key", args.Key,
1218+
"currentReplicas", args.CurrentReplicas,
1219+
"desiredReplicas", args.DesiredReplicas,
1220+
"scaleUpEvents", a.scaleUpEvents[args.Key],
1221+
"scaleDownEvents", a.scaleDownEvents[args.Key])
1222+
11611223
scaleUpLimit := calculateScaleUpLimitWithScalingRules(args.CurrentReplicas, a.scaleUpEvents[args.Key], a.scaleDownEvents[args.Key], args.ScaleUpBehavior)
11621224

11631225
if scaleUpLimit < args.CurrentReplicas {
@@ -1181,6 +1243,13 @@ func (a *HorizontalController) convertDesiredReplicasWithBehaviorRate(args Norma
11811243
defer a.scaleUpEventsLock.RUnlock()
11821244
a.scaleDownEventsLock.RLock()
11831245
defer a.scaleDownEventsLock.RUnlock()
1246+
logger.V(4).Info("Converting desired replicas with behavior rate - scale down",
1247+
"key", args.Key,
1248+
"currentReplicas", args.CurrentReplicas,
1249+
"desiredReplicas", args.DesiredReplicas,
1250+
"scaleUpEvents", a.scaleUpEvents[args.Key],
1251+
"scaleDownEvents", a.scaleDownEvents[args.Key])
1252+
11841253
scaleDownLimit := calculateScaleDownLimitWithBehaviors(args.CurrentReplicas, a.scaleUpEvents[args.Key], a.scaleDownEvents[args.Key], args.ScaleDownBehavior)
11851254

11861255
if scaleDownLimit > args.CurrentReplicas {

pkg/controller/podautoscaler/horizontal_test.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4315,6 +4315,42 @@ func TestScalingWithRules(t *testing.T) {
43154315
expectedReplicas: 16,
43164316
expectedCondition: "ScaleUpLimit",
43174317
},
4318+
{
4319+
name: "scaleUp with percent policy and no previous events, thus scaled up within range",
4320+
scaleUpEvents: generateEventsUniformDistribution([]int{0}, 120),
4321+
scaleDownEvents: generateEventsUniformDistribution([]int{0}, 120),
4322+
specMinReplicas: 3,
4323+
specMaxReplicas: 6,
4324+
scaleUpRules: generateScalingRules(0, 0, 20, 600, 300),
4325+
currentReplicas: 3,
4326+
prenormalizedDesiredReplicas: 4,
4327+
expectedReplicas: 4,
4328+
expectedCondition: "DesiredWithinRange",
4329+
},
4330+
{
4331+
name: "scaleUp with both pod and percent policy and previous scale up events, thus no scale up",
4332+
scaleUpEvents: generateEventsUniformDistribution([]int{2}, 120),
4333+
scaleDownEvents: generateEventsUniformDistribution([]int{0}, 120),
4334+
specMinReplicas: 3,
4335+
specMaxReplicas: 6,
4336+
scaleUpRules: generateScalingRules(1, 600, 20, 600, 300),
4337+
currentReplicas: 3,
4338+
prenormalizedDesiredReplicas: 4,
4339+
expectedReplicas: 3,
4340+
expectedCondition: "ScaleUpLimit",
4341+
},
4342+
{
4343+
name: "scaleUp with percent policy and previous events but still be able to scale up with limited rate",
4344+
scaleUpEvents: generateEventsUniformDistribution([]int{1}, 600),
4345+
scaleDownEvents: generateEventsUniformDistribution([]int{0}, 600),
4346+
specMinReplicas: 3,
4347+
specMaxReplicas: 20,
4348+
scaleUpRules: generateScalingRules(0, 0, 20, 600, 300),
4349+
currentReplicas: 10,
4350+
prenormalizedDesiredReplicas: 12,
4351+
expectedReplicas: 11,
4352+
expectedCondition: "ScaleUpLimit",
4353+
},
43184354
// ScaleDown with PeriodSeconds usage
43194355
{
43204356
name: "scaleDown with default policy and previous events",
@@ -4486,6 +4522,7 @@ func TestScalingWithRules(t *testing.T) {
44864522
},
44874523
}
44884524

4525+
tCtx := ktesting.Init(t)
44894526
for _, tc := range tests {
44904527
t.Run(tc.name, func(t *testing.T) {
44914528

@@ -4510,7 +4547,7 @@ func TestScalingWithRules(t *testing.T) {
45104547
CurrentReplicas: tc.currentReplicas,
45114548
}
45124549

4513-
replicas, condition, _ := hc.convertDesiredReplicasWithBehaviorRate(arg)
4550+
replicas, condition, _ := hc.convertDesiredReplicasWithBehaviorRate(tCtx, arg)
45144551
assert.Equal(t, tc.expectedReplicas, replicas, "expected replicas do not match with converted replicas")
45154552
assert.Equal(t, tc.expectedCondition, condition, "HPA condition does not match with expected condition")
45164553
})
@@ -4865,6 +4902,7 @@ func TestNormalizeDesiredReplicasWithBehavior(t *testing.T) {
48654902
scaleDownStabilizationWindowSeconds: 300,
48664903
},
48674904
}
4905+
tCtx := ktesting.Init(t)
48684906
for _, tc := range tests {
48694907
t.Run(tc.name, func(t *testing.T) {
48704908
hc := HorizontalController{
@@ -4883,7 +4921,7 @@ func TestNormalizeDesiredReplicasWithBehavior(t *testing.T) {
48834921
StabilizationWindowSeconds: &tc.scaleDownStabilizationWindowSeconds,
48844922
},
48854923
}
4886-
r, _, _ := hc.stabilizeRecommendationWithBehaviors(arg)
4924+
r, _, _ := hc.stabilizeRecommendationWithBehaviors(tCtx, arg)
48874925
assert.Equal(t, tc.expectedStabilizedReplicas, r, "expected replicas do not match")
48884926
if tc.expectedRecommendations != nil {
48894927
if !assert.Len(t, hc.recommendations[tc.key], len(tc.expectedRecommendations), "stored recommendations differ in length") {

0 commit comments

Comments
 (0)