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

make DeepEqual judgments more efficient #4827

Merged
merged 1 commit into from
Apr 16, 2024

Conversation

zhzhuang-zju
Copy link
Contributor

What type of PR is this?
/kind cleanup

What this PR does / why we need it:
Function CreateOrUpdateClusterObject is used to create cluster object in karmada control plane, or update it if it has been existed.

karmada/pkg/util/cluster.go

Lines 145 to 151 in da7689f

if exist {
if reflect.DeepEqual(cluster.Spec, clusterObj.Spec) {
klog.Warningf("Cluster(%s) already exist and newest", clusterObj.Name)
return cluster, nil
}
mutate(cluster)
cluster, err = updateCluster(controlPlaneClient, cluster)

reflect.DeepEqual(cluster.Spec, clusterObj.Spec) It doesn't make sense to compare the cluster to the clusterobj, it should be the cluster to the mutate(cluster) in order to correctly determine if an update is needed.

Which issue(s) this PR fixes:
Fixes #

Special notes for your reviewer:

Does this PR introduce a user-facing change?:


@karmada-bot karmada-bot added the kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. label Apr 12, 2024
@karmada-bot karmada-bot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Apr 12, 2024
Comment on lines 146 to 147
if reflect.DeepEqual(cluster.Spec, clusterObj.Spec) {
klog.Warningf("Cluster(%s) already exist and newest", clusterObj.Name)
Copy link
Member

Choose a reason for hiding this comment

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

I got it, so clusterObj.Spec is a default value, it will always not deep equal to cluster.Spec, it is a redundant comparison.

what we should compare is cluster.Spec with cluster after mutated, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes~you're right

Comment on lines 139 to 147
// if cluster object has been existed, update it if necessary.
func CreateOrUpdateClusterObject(controlPlaneClient karmadaclientset.Interface, clusterName string, mutate func(*clusterv1alpha1.Cluster)) (*clusterv1alpha1.Cluster, error) {
cluster, exist, err := GetClusterWithKarmadaClient(controlPlaneClient, clusterName)
if err != nil {
return nil, err
}
if exist {
if reflect.DeepEqual(cluster.Spec, clusterObj.Spec) {
klog.Warningf("Cluster(%s) already exist and newest", clusterObj.Name)
clusterCopy := cluster.DeepCopy()
mutate(cluster)
Copy link
Member

Choose a reason for hiding this comment

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

generally looks good, but I may prefer improving the code style. I think this function although named CreateOrUpdateClusterObject actually does two things: BuildCluster and CreateOrUpdateCluster, may be we can split into two functions.

Besides, I searched the key words CreateOrUpdate in k8s source code, his code is written like this:

  • before k8s release-1.30:

https://github.com/kubernetes/kubernetes/blob/3df26e386335110e13b32c2f679a3cd2ee4bd8cb/cmd/kubeadm/app/util/apiclient/idempotency.go#L147-L159

  • after k8s release-1.30:

https://github.com/kubernetes/kubernetes/blob/698ad6630c56ffc27946f766b5c77e1f16482a77/cmd/kubeadm/app/util/apiclient/idempotency.go#L190-L213

The way k8s code is written and its code style evolution from v1.29 to v1.30 version could be used as a more mainstream reference.

Copy link
Member

Choose a reason for hiding this comment

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

thanks for your important commit again, above is just for reference only.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks for your import suggestion!

	err := wait.PollUntilContextTimeout(context.Background(),
		apiCallRetryInterval, kubeadmapi.GetActiveTimeouts().KubernetesAPICall.Duration,
		true, func(_ context.Context) (bool, error) {
			ctx := context.Background()
			if _, err := client.AppsV1().Deployments(deploy.ObjectMeta.Namespace).Create(ctx, deploy, metav1.CreateOptions{}); err != nil {
				if !apierrors.IsAlreadyExists(err) {
					lastError = errors.Wrap(err, "unable to create Deployment")
					return false, nil
				}
				if _, err := client.AppsV1().Deployments(deploy.ObjectMeta.Namespace).Update(ctx, deploy, metav1.UpdateOptions{}); err != nil {
					lastError = errors.Wrap(err, "unable to update Deployment")
					return false, nil
				}
			}
			return true, nil
		})

I don't understand where the resourceversion of the deploy is initialized, I checked the call chain of this function, but I didn't find the corresponding place. @chaosi-zju

Copy link
Contributor Author

Choose a reason for hiding this comment

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

func CreateOrUpdate(ctx context.Context, c client.Client, obj client.Object, f MutateFn) (OperationResult, error) {
key := client.ObjectKeyFromObject(obj)
if err := c.Get(ctx, key, obj); err != nil {
if !apierrors.IsNotFound(err) {
return OperationResultNone, err
}
if err := mutate(f, key, obj); err != nil {
return OperationResultNone, err
}
if err := c.Create(ctx, obj); err != nil {
return OperationResultNone, err
}
return OperationResultCreated, nil
}
existing := obj.DeepCopyObject()
if err := mutate(f, key, obj); err != nil {
return OperationResultNone, err
}
if equality.Semantic.DeepEqual(existing, obj) {
return OperationResultNone, nil
}
if err := c.Update(ctx, obj); err != nil {
return OperationResultNone, err
}
return OperationResultUpdated, nil
}

How about this? @chaosi-zju

@XiShanYongYe-Chang
Copy link
Member

/assign

@karmada-bot karmada-bot added size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Apr 15, 2024
@zhzhuang-zju
Copy link
Contributor Author

cc @XiShanYongYe-Chang @chaosi-zju

Copy link
Member

@XiShanYongYe-Chang XiShanYongYe-Chang left a comment

Choose a reason for hiding this comment

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

Thanks
/lgtm

@karmada-bot karmada-bot added the lgtm Indicates that a PR is ready to be merged. label Apr 15, 2024
Copy link
Member

@chaunceyjiang chaunceyjiang left a comment

Choose a reason for hiding this comment

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

Nice finding.
/LGTM

@karmada-bot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: chaunceyjiang

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@karmada-bot karmada-bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 16, 2024
@karmada-bot karmada-bot merged commit 7c9bc73 into karmada-io:master Apr 16, 2024
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. lgtm Indicates that a PR is ready to be merged. size/XS Denotes a PR that changes 0-9 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants