Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added hpa update predicate to reconcile a scaledobject only if hpa spec,label or annotation is changed #5282

Conversation

deefreak
Copy link
Contributor

This change fixes #5281 .
We have verified that scaledobject reconcilation is triggered only when hpa spec/label changes or hpa is deleted.
I raise this PR to get early feedback and automated tests will be added soon.

@deefreak deefreak requested a review from a team as a code owner December 12, 2023 12:09
Copy link

Thank you for your contribution! 🙏 We will review your PR as soon as possible.

While you are waiting, make sure to:

Learn more about:

Copy link
Member

@zroubalik zroubalik left a comment

Choose a reason for hiding this comment

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

We should probably add check for annotations here as well:

func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
return err
}
// DeepDerivative ignores extra entries in arrays which makes removing the last trigger not update things, so trigger and update any time the metrics count is different.
if len(hpa.Spec.Metrics) != len(foundHpa.Spec.Metrics) || !equality.Semantic.DeepDerivative(hpa.Spec, foundHpa.Spec) {
logger.V(1).Info("Found difference in the HPA spec accordint to ScaledObject", "currentHPA", foundHpa.Spec, "newHPA", hpa.Spec)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.Spec = hpa.Spec
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
if !equality.Semantic.DeepDerivative(hpa.ObjectMeta.Labels, foundHpa.ObjectMeta.Labels) {
logger.V(1).Info("Found difference in the HPA labels accordint to ScaledObject", "currentHPA", foundHpa.ObjectMeta.Labels, "newHPA", hpa.ObjectMeta.Labels)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.ObjectMeta.Labels = hpa.ObjectMeta.Labels
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
return nil

controllers/keda/scaledobject_controller.go Outdated Show resolved Hide resolved
controllers/keda/util/predicate.go Outdated Show resolved Hide resolved
controllers/keda/util/predicate.go Outdated Show resolved Hide resolved
@deefreak
Copy link
Contributor Author

deefreak commented Dec 13, 2023

We should probably add check for annotations here as well:

func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
return err
}
// DeepDerivative ignores extra entries in arrays which makes removing the last trigger not update things, so trigger and update any time the metrics count is different.
if len(hpa.Spec.Metrics) != len(foundHpa.Spec.Metrics) || !equality.Semantic.DeepDerivative(hpa.Spec, foundHpa.Spec) {
logger.V(1).Info("Found difference in the HPA spec accordint to ScaledObject", "currentHPA", foundHpa.Spec, "newHPA", hpa.Spec)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.Spec = hpa.Spec
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
if !equality.Semantic.DeepDerivative(hpa.ObjectMeta.Labels, foundHpa.ObjectMeta.Labels) {
logger.V(1).Info("Found difference in the HPA labels accordint to ScaledObject", "currentHPA", foundHpa.ObjectMeta.Labels, "newHPA", hpa.ObjectMeta.Labels)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.ObjectMeta.Labels = hpa.ObjectMeta.Labels
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
return nil

I am thinking that as scaledobject controller is not adding any new annotation to the HPA and HPA controller might add some annotation to it, so it should be fine to remove predicate.AnnotationChangedPredicate{} and leave this piece of code as it is.

We are now on autoscaling/v2 where currently no annotation is being added by hpa controller but i saw in autoscaling/v1 that the current metrics were part of the annotations and they kept changing.
So, it would be better to not use this predicate as there may be changes in the future in HPA controller updating annotations.

…ec,label or ownerRefernce is changed

Signed-off-by: deefreak <deepakgts2@gmail.com>
…ec,label or annotation is changed

Signed-off-by: deefreak <deepakgts2@gmail.com>
…ec,label or annotation is changed

Signed-off-by: deefreak <deepakgts2@gmail.com>
…ec or label is changed

Signed-off-by: deefreak <deepakgts2@gmail.com>
@deefreak deefreak force-pushed the redundant-scaledobject-reconciles-by-HPA-status-updates branch from c1011c6 to 00193f8 Compare December 13, 2023 08:28
@JorTurFer
Copy link
Member

JorTurFer commented Dec 13, 2023

We should probably add check for annotations here as well:

func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
return err
}
// DeepDerivative ignores extra entries in arrays which makes removing the last trigger not update things, so trigger and update any time the metrics count is different.
if len(hpa.Spec.Metrics) != len(foundHpa.Spec.Metrics) || !equality.Semantic.DeepDerivative(hpa.Spec, foundHpa.Spec) {
logger.V(1).Info("Found difference in the HPA spec accordint to ScaledObject", "currentHPA", foundHpa.Spec, "newHPA", hpa.Spec)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.Spec = hpa.Spec
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
if !equality.Semantic.DeepDerivative(hpa.ObjectMeta.Labels, foundHpa.ObjectMeta.Labels) {
logger.V(1).Info("Found difference in the HPA labels accordint to ScaledObject", "currentHPA", foundHpa.ObjectMeta.Labels, "newHPA", hpa.ObjectMeta.Labels)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.ObjectMeta.Labels = hpa.ObjectMeta.Labels
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
return nil

I am thinking that as scaledobject controller is not adding any new annotation to the HPA and HPA controller might add some annotation to it, so it should be fine to remove predicate.AnnotationChangedPredicate{} and leave this piece of code as it is.

We are now on autoscaling/v2 where currently no annotation is being added by hpa controller but i saw in autoscaling/v1 that the current metrics were part of the annotations and they kept changing. So, it would be better to not use this predicate as there may be changes in the future in HPA controller updating annotations.

We are propagating the ScaledObject labels/annotations so I'd say that we have to keep the predicate:

ObjectMeta: metav1.ObjectMeta{
Name: getHPAName(scaledObject),
Namespace: scaledObject.Namespace,
Labels: labels,
Annotations: scaledObject.Annotations,
},

@deefreak
Copy link
Contributor Author

We should probably add check for annotations here as well:

func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
return err
}
// DeepDerivative ignores extra entries in arrays which makes removing the last trigger not update things, so trigger and update any time the metrics count is different.
if len(hpa.Spec.Metrics) != len(foundHpa.Spec.Metrics) || !equality.Semantic.DeepDerivative(hpa.Spec, foundHpa.Spec) {
logger.V(1).Info("Found difference in the HPA spec accordint to ScaledObject", "currentHPA", foundHpa.Spec, "newHPA", hpa.Spec)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.Spec = hpa.Spec
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
if !equality.Semantic.DeepDerivative(hpa.ObjectMeta.Labels, foundHpa.ObjectMeta.Labels) {
logger.V(1).Info("Found difference in the HPA labels accordint to ScaledObject", "currentHPA", foundHpa.ObjectMeta.Labels, "newHPA", hpa.ObjectMeta.Labels)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.ObjectMeta.Labels = hpa.ObjectMeta.Labels
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
return nil

I am thinking that as scaledobject controller is not adding any new annotation to the HPA and HPA controller might add some annotation to it, so it should be fine to remove predicate.AnnotationChangedPredicate{} and leave this piece of code as it is.
We are now on autoscaling/v2 where currently no annotation is being added by hpa controller but i saw in autoscaling/v1 that the current metrics were part of the annotations and they kept changing. So, it would be better to not use this predicate as there may be changes in the future in HPA controller updating annotations.

We are propagating the ScaledObject labels/annotations so I'd say that we have to keep the predicate:

ObjectMeta: metav1.ObjectMeta{
Name: getHPAName(scaledObject),
Namespace: scaledObject.Namespace,
Labels: labels,
Annotations: scaledObject.Annotations,
},

Sorry, my bad , missed this. Also, confirmed that currently hpa controller is not adding any annotations to the hpa object. So, it should be fine to add the annotation changed predicate . I will also then make changes to the

func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
return err
}
// DeepDerivative ignores extra entries in arrays which makes removing the last trigger not update things, so trigger and update any time the metrics count is different.
if len(hpa.Spec.Metrics) != len(foundHpa.Spec.Metrics) || !equality.Semantic.DeepDerivative(hpa.Spec, foundHpa.Spec) {
logger.V(1).Info("Found difference in the HPA spec accordint to ScaledObject", "currentHPA", foundHpa.Spec, "newHPA", hpa.Spec)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.Spec = hpa.Spec
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
if !equality.Semantic.DeepDerivative(hpa.ObjectMeta.Labels, foundHpa.ObjectMeta.Labels) {
logger.V(1).Info("Found difference in the HPA labels accordint to ScaledObject", "currentHPA", foundHpa.ObjectMeta.Labels, "newHPA", hpa.ObjectMeta.Labels)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.ObjectMeta.Labels = hpa.ObjectMeta.Labels
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
return nil

to add check for annotations as well.
I hope this is fine @JorTurFer

@zroubalik
Copy link
Member

We should probably add check for annotations here as well:

func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
return err
}
// DeepDerivative ignores extra entries in arrays which makes removing the last trigger not update things, so trigger and update any time the metrics count is different.
if len(hpa.Spec.Metrics) != len(foundHpa.Spec.Metrics) || !equality.Semantic.DeepDerivative(hpa.Spec, foundHpa.Spec) {
logger.V(1).Info("Found difference in the HPA spec accordint to ScaledObject", "currentHPA", foundHpa.Spec, "newHPA", hpa.Spec)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.Spec = hpa.Spec
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
if !equality.Semantic.DeepDerivative(hpa.ObjectMeta.Labels, foundHpa.ObjectMeta.Labels) {
logger.V(1).Info("Found difference in the HPA labels accordint to ScaledObject", "currentHPA", foundHpa.ObjectMeta.Labels, "newHPA", hpa.ObjectMeta.Labels)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.ObjectMeta.Labels = hpa.ObjectMeta.Labels
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
return nil

I am thinking that as scaledobject controller is not adding any new annotation to the HPA and HPA controller might add some annotation to it, so it should be fine to remove predicate.AnnotationChangedPredicate{} and leave this piece of code as it is.
We are now on autoscaling/v2 where currently no annotation is being added by hpa controller but i saw in autoscaling/v1 that the current metrics were part of the annotations and they kept changing. So, it would be better to not use this predicate as there may be changes in the future in HPA controller updating annotations.

We are propagating the ScaledObject labels/annotations so I'd say that we have to keep the predicate:

ObjectMeta: metav1.ObjectMeta{
Name: getHPAName(scaledObject),
Namespace: scaledObject.Namespace,
Labels: labels,
Annotations: scaledObject.Annotations,
},

Sorry, my bad , missed this. Also, confirmed that currently hpa controller is not adding any annotations to the hpa object. So, it should be fine to add the annotation changed predicate . I will also then make changes to the

func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
return err
}
// DeepDerivative ignores extra entries in arrays which makes removing the last trigger not update things, so trigger and update any time the metrics count is different.
if len(hpa.Spec.Metrics) != len(foundHpa.Spec.Metrics) || !equality.Semantic.DeepDerivative(hpa.Spec, foundHpa.Spec) {
logger.V(1).Info("Found difference in the HPA spec accordint to ScaledObject", "currentHPA", foundHpa.Spec, "newHPA", hpa.Spec)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.Spec = hpa.Spec
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
if !equality.Semantic.DeepDerivative(hpa.ObjectMeta.Labels, foundHpa.ObjectMeta.Labels) {
logger.V(1).Info("Found difference in the HPA labels accordint to ScaledObject", "currentHPA", foundHpa.ObjectMeta.Labels, "newHPA", hpa.ObjectMeta.Labels)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.ObjectMeta.Labels = hpa.ObjectMeta.Labels
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
return nil

to add check for annotations as well. I hope this is fine @JorTurFer

yes please

…ec,label or annotation is changed

Signed-off-by: deefreak <deepakgts2@gmail.com>
@deefreak
Copy link
Contributor Author

deefreak commented Dec 13, 2023

@zroubalik I have done all the commented changes. Have added unit tests to test the HPASpecChangedPredicate as well.

Copy link
Member

@zroubalik zroubalik left a comment

Choose a reason for hiding this comment

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

Looking good, I wonder whether we can test that updates to status don't trigger a reconcile loop?

@zroubalik
Copy link
Member

zroubalik commented Dec 13, 2023

/run-e2e
Update: You can check the progress here

@deefreak
Copy link
Contributor Author

Looking good, I wonder whether we can test that updates to status don't trigger a reconcile loop?

As there will be no change in scaledobjects, it will be really hard to test through a unit test. I mean there will be nothing to compare for before and after reconcile. Although i have compared it on a cluster with more than 100+ scaledobjects and there were no reconcilations for hpa status updates(.status.currentMetrics) after applying this fix.

@zroubalik
Copy link
Member

Looking good, I wonder whether we can test that updates to status don't trigger a reconcile loop?

As there will be no change in scaledobjects, it will be really hard to test through a unit test. I mean there will be nothing to compare for before and after reconcile. Although i have compared it on a cluster with more than 100+ scaledobjects and there were no reconcilations for hpa status updates(.status.currentMetrics) after applying this fix.

yeah, agree

@deefreak
Copy link
Contributor Author

deefreak commented Dec 13, 2023

We should probably add check for annotations here as well:

func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
return err
}
// DeepDerivative ignores extra entries in arrays which makes removing the last trigger not update things, so trigger and update any time the metrics count is different.
if len(hpa.Spec.Metrics) != len(foundHpa.Spec.Metrics) || !equality.Semantic.DeepDerivative(hpa.Spec, foundHpa.Spec) {
logger.V(1).Info("Found difference in the HPA spec accordint to ScaledObject", "currentHPA", foundHpa.Spec, "newHPA", hpa.Spec)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.Spec = hpa.Spec
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
if !equality.Semantic.DeepDerivative(hpa.ObjectMeta.Labels, foundHpa.ObjectMeta.Labels) {
logger.V(1).Info("Found difference in the HPA labels accordint to ScaledObject", "currentHPA", foundHpa.ObjectMeta.Labels, "newHPA", hpa.ObjectMeta.Labels)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.ObjectMeta.Labels = hpa.ObjectMeta.Labels
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
return nil

I am thinking that as scaledobject controller is not adding any new annotation to the HPA and HPA controller might add some annotation to it, so it should be fine to remove predicate.AnnotationChangedPredicate{} and leave this piece of code as it is.
We are now on autoscaling/v2 where currently no annotation is being added by hpa controller but i saw in autoscaling/v1 that the current metrics were part of the annotations and they kept changing. So, it would be better to not use this predicate as there may be changes in the future in HPA controller updating annotations.

We are propagating the ScaledObject labels/annotations so I'd say that we have to keep the predicate:

ObjectMeta: metav1.ObjectMeta{
Name: getHPAName(scaledObject),
Namespace: scaledObject.Namespace,
Labels: labels,
Annotations: scaledObject.Annotations,
},

Sorry, my bad , missed this. Also, confirmed that currently hpa controller is not adding any annotations to the hpa object. So, it should be fine to add the annotation changed predicate . I will also then make changes to the

func (r *ScaledObjectReconciler) updateHPAIfNeeded(ctx context.Context, logger logr.Logger, scaledObject *kedav1alpha1.ScaledObject, foundHpa *autoscalingv2.HorizontalPodAutoscaler, gvkr *kedav1alpha1.GroupVersionKindResource) error {
hpa, err := r.newHPAForScaledObject(ctx, logger, scaledObject, gvkr)
if err != nil {
logger.Error(err, "Failed to create new HPA resource", "HPA.Namespace", scaledObject.Namespace, "HPA.Name", getHPAName(scaledObject))
return err
}
// DeepDerivative ignores extra entries in arrays which makes removing the last trigger not update things, so trigger and update any time the metrics count is different.
if len(hpa.Spec.Metrics) != len(foundHpa.Spec.Metrics) || !equality.Semantic.DeepDerivative(hpa.Spec, foundHpa.Spec) {
logger.V(1).Info("Found difference in the HPA spec accordint to ScaledObject", "currentHPA", foundHpa.Spec, "newHPA", hpa.Spec)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.Spec = hpa.Spec
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
if !equality.Semantic.DeepDerivative(hpa.ObjectMeta.Labels, foundHpa.ObjectMeta.Labels) {
logger.V(1).Info("Found difference in the HPA labels accordint to ScaledObject", "currentHPA", foundHpa.ObjectMeta.Labels, "newHPA", hpa.ObjectMeta.Labels)
if err = r.Client.Update(ctx, hpa); err != nil {
foundHpa.ObjectMeta.Labels = hpa.ObjectMeta.Labels
logger.Error(err, "Failed to update HPA", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
return err
}
logger.Info("Updated HPA according to ScaledObject", "HPA.Namespace", foundHpa.Namespace, "HPA.Name", foundHpa.Name)
}
return nil

to add check for annotations as well. I hope this is fine @JorTurFer

yes please

I added the check for annotations as similar to the labels.
Wrote a test for reconciliation of scaledobject when annotation is changed, noticed that equality.Semantic.DeepDerative(a1,a2 interface{}) returns true if a1 == nil .

when I was testing, the hpa created through scaledobject has no annotations, thus annotation was nil and therefore adding an annotation to the hpa was not reconciling again.

So, we should add this condition, verified that this is working
if (hpa.ObjectMeta.Annotations == nil && foundHpa.ObjectMeta.Annotations != nil) ||
!equality.Semantic.DeepDerivative(hpa.ObjectMeta.Annotations, foundHpa.ObjectMeta.Annotations)

@zroubalik @JorTurFer what do you think?

@zroubalik
Copy link
Member

Good catch, please do that!

…ec,label or annotation is changed

Signed-off-by: deefreak <deepakgts2@gmail.com>
@deefreak
Copy link
Contributor Author

Good catch, please do that!

Done and added tests for label and annotation changes as well.

deefreak and others added 4 commits December 14, 2023 13:47
…ec,label or annotation is changed

Signed-off-by: deefreak <deepakgts2@gmail.com>
…s-by-HPA-status-updates' into redundant-scaledobject-reconciles-by-HPA-status-updates
@zroubalik
Copy link
Member

zroubalik commented Dec 14, 2023

/run-e2e
Update: You can check the progress here

@deefreak
Copy link
Contributor Author

@zroubalik I see this run-e2e is failing . Any idea how to fix this?

@JorTurFer
Copy link
Member

JorTurFer commented Dec 15, 2023

/run-e2e
Update: You can check the progress here

@JorTurFer
Copy link
Member

@zroubalik I see this run-e2e is failing . Any idea how to fix this?

I think that it was an unrelated issue

@deefreak
Copy link
Contributor Author

Given checks have passed, can it be approved now?

@zroubalik zroubalik merged commit 99a1fe5 into kedacore:main Dec 15, 2023
19 checks passed
toniiiik pushed a commit to toniiiik/keda that referenced this pull request Jan 15, 2024
…ec,label or annotation is changed (kedacore#5282)

Signed-off-by: deefreak <deepakgts2@gmail.com>
Signed-off-by: anton.lysina <alysina@gmail.com>
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.

ScaledObject reconciliation delay due to redundant reconciles triggered by HPA status updates
3 participants