forked from kedacore/keda
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ScaledJob: introduce
MultipleScalersCalculation
(kedacore#2016)
* Fix bug of ScaledJob multiple triggers Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * fix pre-commit issue Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Update logs on scale handler and add crds Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * update change log Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Add test case for exceeding MaxReplicaCount in sum Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * fix pre-commit Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Update pkg/scaling/scale_handler.go Co-authored-by: Zbynek Roubalik <726523+zroubalik@users.noreply.github.com> Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Update pkg/scaling/scale_handler.go Co-authored-by: Zbynek Roubalik <726523+zroubalik@users.noreply.github.com> Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * revert version and refactor scaling logic Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Change option name from MultipleScalersOption to MultipleScalersCalculation Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * update changelog for multiple triggers section Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Error Handling Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Refactor logging Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * remove scaledjob prometheus Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Update pkg/scaling/scaledjob/scale_metrics.go Co-authored-by: Zbynek Roubalik <726523+zroubalik@users.noreply.github.com> Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Update pkg/scaling/scaledjob/scale_metrics.go Co-authored-by: Zbynek Roubalik <726523+zroubalik@users.noreply.github.com> Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Update pkg/scaling/scaledjob/scale_metrics.go Co-authored-by: Zbynek Roubalik <726523+zroubalik@users.noreply.github.com> Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Update pkg/scaling/scaledjob/scale_metrics_test.go Co-authored-by: Zbynek Roubalik <726523+zroubalik@users.noreply.github.com> Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Update pkg/scaling/scaledjob/scale_metrics.go Co-authored-by: Zbynek Roubalik <726523+zroubalik@users.noreply.github.com> Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Update CHANGELOG.md Co-authored-by: Zbynek Roubalik <726523+zroubalik@users.noreply.github.com> Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * Move section to new section Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> * fix pre-commit white space issue Signed-off-by: Tsuyoshi Ushio <ushio@simplearchitect.com> Co-authored-by: Zbynek Roubalik <726523+zroubalik@users.noreply.github.com> Signed-off-by: nilayasiktoprak <nilayasiktoprak@gmail.com>
- Loading branch information
1 parent
80d139e
commit 588b950
Showing
7 changed files
with
397 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package scaledjob | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/go-logr/logr" | ||
"k8s.io/api/autoscaling/v2beta2" | ||
corev1 "k8s.io/api/core/v1" | ||
"k8s.io/client-go/tools/record" | ||
logf "sigs.k8s.io/controller-runtime/pkg/log" | ||
|
||
kedav1alpha1 "github.com/kedacore/keda/v2/apis/keda/v1alpha1" | ||
"github.com/kedacore/keda/v2/pkg/eventreason" | ||
"github.com/kedacore/keda/v2/pkg/scalers" | ||
) | ||
|
||
type scalerMetrics struct { | ||
queueLength int64 | ||
maxValue int64 | ||
isActive bool | ||
} | ||
|
||
// GetScaleMetrics gets the metrics for decision making of scaling. | ||
func GetScaleMetrics(ctx context.Context, scalers []scalers.Scaler, scaledJob *kedav1alpha1.ScaledJob, recorder record.EventRecorder) (bool, int64, int64) { | ||
var queueLength int64 | ||
var maxValue int64 | ||
isActive := false | ||
|
||
logger := logf.Log.WithName("scalemetrics") | ||
scalersMetrics := getScalersMetrics(ctx, scalers, scaledJob, logger, recorder) | ||
switch scaledJob.Spec.ScalingStrategy.MultipleScalersCalculation { | ||
case "min": | ||
for _, metrics := range scalersMetrics { | ||
if (queueLength == 0 || metrics.queueLength < queueLength) && metrics.isActive { | ||
queueLength = metrics.queueLength | ||
maxValue = metrics.maxValue | ||
isActive = metrics.isActive | ||
} | ||
} | ||
case "avg": | ||
queueLengthSum := int64(0) | ||
maxValueSum := int64(0) | ||
length := 0 | ||
for _, metrics := range scalersMetrics { | ||
if metrics.isActive { | ||
queueLengthSum += metrics.queueLength | ||
maxValueSum += metrics.maxValue | ||
isActive = metrics.isActive | ||
length++ | ||
} | ||
} | ||
if length != 0 { | ||
queueLength = divideWithCeil(queueLengthSum, int64(length)) | ||
maxValue = divideWithCeil(maxValueSum, int64(length)) | ||
} | ||
case "sum": | ||
for _, metrics := range scalersMetrics { | ||
if metrics.isActive { | ||
queueLength += metrics.queueLength | ||
maxValue += metrics.maxValue | ||
isActive = metrics.isActive | ||
} | ||
} | ||
default: // max | ||
for _, metrics := range scalersMetrics { | ||
if metrics.queueLength > queueLength && metrics.isActive { | ||
queueLength = metrics.queueLength | ||
maxValue = metrics.maxValue | ||
isActive = metrics.isActive | ||
} | ||
} | ||
} | ||
maxValue = min(scaledJob.MaxReplicaCount(), maxValue) | ||
logger.V(1).WithValues("ScaledJob", scaledJob.Name).Info("Checking if ScaleJob scalers are active", "isActive", isActive, "maxValue", maxValue, "MultipleScalersCalculation", scaledJob.Spec.ScalingStrategy.MultipleScalersCalculation) | ||
|
||
return isActive, queueLength, maxValue | ||
} | ||
|
||
func getScalersMetrics(ctx context.Context, scalers []scalers.Scaler, scaledJob *kedav1alpha1.ScaledJob, logger logr.Logger, recorder record.EventRecorder) []scalerMetrics { | ||
scalersMetrics := []scalerMetrics{} | ||
|
||
for _, scaler := range scalers { | ||
var queueLength int64 | ||
var targetAverageValue int64 | ||
isActive := false | ||
maxValue := int64(0) | ||
scalerType := fmt.Sprintf("%T:", scaler) | ||
|
||
scalerLogger := logger.WithValues("ScaledJob", scaledJob.Name, "Scaler", scalerType) | ||
|
||
metricSpecs := scaler.GetMetricSpecForScaling() | ||
|
||
// skip scaler that doesn't return any metric specs (usually External scaler with incorrect metadata) | ||
// or skip cpu/memory resource scaler | ||
if len(metricSpecs) < 1 || metricSpecs[0].External == nil { | ||
continue | ||
} | ||
|
||
isTriggerActive, err := scaler.IsActive(ctx) | ||
if err != nil { | ||
scalerLogger.V(1).Info("Error getting scaler.IsActive, but continue", "Error", err) | ||
recorder.Event(scaledJob, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) | ||
scaler.Close() | ||
continue | ||
} | ||
|
||
targetAverageValue = getTargetAverageValue(metricSpecs) | ||
|
||
metrics, err := scaler.GetMetrics(ctx, "queueLength", nil) | ||
if err != nil { | ||
scalerLogger.V(1).Info("Error getting scaler metrics, but continue", "Error", err) | ||
recorder.Event(scaledJob, corev1.EventTypeWarning, eventreason.KEDAScalerFailed, err.Error()) | ||
scaler.Close() | ||
continue | ||
} | ||
|
||
var metricValue int64 | ||
|
||
for _, m := range metrics { | ||
if m.MetricName == "queueLength" { | ||
metricValue, _ = m.Value.AsInt64() | ||
queueLength += metricValue | ||
} | ||
} | ||
scalerLogger.V(1).Info("Scaler Metric value", "isTriggerActive", isTriggerActive, "queueLength", queueLength, "targetAverageValue", targetAverageValue) | ||
|
||
scaler.Close() | ||
|
||
if isTriggerActive { | ||
isActive = true | ||
} | ||
|
||
if targetAverageValue != 0 { | ||
maxValue = min(scaledJob.MaxReplicaCount(), divideWithCeil(queueLength, targetAverageValue)) | ||
} | ||
scalersMetrics = append(scalersMetrics, scalerMetrics{ | ||
queueLength: queueLength, | ||
maxValue: maxValue, | ||
isActive: isActive, | ||
}) | ||
} | ||
return scalersMetrics | ||
} | ||
|
||
func getTargetAverageValue(metricSpecs []v2beta2.MetricSpec) int64 { | ||
var targetAverageValue int64 | ||
var metricValue int64 | ||
var flag bool | ||
for _, metric := range metricSpecs { | ||
if metric.External.Target.AverageValue == nil { | ||
metricValue = 0 | ||
} else { | ||
metricValue, flag = metric.External.Target.AverageValue.AsInt64() | ||
if !flag { | ||
metricValue = 0 | ||
} | ||
} | ||
|
||
targetAverageValue += metricValue | ||
} | ||
count := int64(len(metricSpecs)) | ||
if count != 0 { | ||
return targetAverageValue / count | ||
} | ||
return 0 | ||
} | ||
|
||
func divideWithCeil(x, y int64) int64 { | ||
ans := x / y | ||
reminder := x % y | ||
if reminder != 0 { | ||
return ans + 1 | ||
} | ||
return ans | ||
} | ||
|
||
// Min function for int64 | ||
func min(x, y int64) int64 { | ||
if x > y { | ||
return y | ||
} | ||
return x | ||
} |
Oops, something went wrong.