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

fix deep equal check failure in CreateOrUpdateWork(), by replace the marshaler #5939

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

zach593
Copy link
Contributor

@zach593 zach593 commented Dec 11, 2024

What type of PR is this?

/kind bug

What this PR does / why we need it:
see #5938

Which issue(s) this PR fixes:
Fixes #5938

Special notes for your reviewer:

Does this PR introduce a user-facing change?:

NONE

@karmada-bot karmada-bot added the kind/bug Categorizes issue or PR as related to a bug. label Dec 11, 2024
@karmada-bot karmada-bot added the size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. label Dec 11, 2024
@codecov-commenter
Copy link

codecov-commenter commented Dec 11, 2024

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 48.37%. Comparing base (8313813) to head (ce1ca99).

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #5939   +/-   ##
=======================================
  Coverage   48.37%   48.37%           
=======================================
  Files         666      666           
  Lines       54831    54831           
=======================================
  Hits        26524    26524           
  Misses      26590    26590           
  Partials     1717     1717           
Flag Coverage Δ
unittests 48.37% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@chaosi-zju
Copy link
Member

Hi @zach593, I am trying to use this issue to design a performance monitoring case, my case is as follows:

  1. Record Initial Workqueue Metrics: Capture metrics before changes.
  2. Create N Deployments and N Policies: Set up deployments and policies, then wait for propagation to member1 and member2 clusters.
  3. Create N OverridePolicies: Implement overridepolicies to modify deployment labels for member1 cluster only. (Prior to this PR, deployments in both clusters would be updated, but after this PR, only the member1 cluster's deployments will be updated. This PR will reduce the update time for each reconciliation).
  4. Record End Workqueue Metrics: Capture metrics after changes and calculate total reconciliation time.

Do you think there are better optimization opportunities for my case?

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.

LGTM, the current pr resolves the deepcopy failure issue and dose not introduce new performance risks.

About the kind/bug label. do you expect to sync the current pr to the previous release versions? If we don't need to, maybe we can modify the label.

@zach593
Copy link
Contributor Author

zach593 commented Jan 8, 2025

Do you think there are better optimization opportunities for my case?

@chaosi-zju As we discussed in zoom, for the performance optimization, this PR is not enough, I may need another 2 PRs to complete this goal. Since I need more than one PR, I created #6017 to track it, and we can have further discussion in that issue.

@zach593
Copy link
Contributor Author

zach593 commented Jan 8, 2025

About the kind/bug label. do you expect to sync the current pr to the previous release versions? If we don't need to, maybe we can modify the label.

Sure, can you help with that? I'm not familiar with commands with /

@zach593
Copy link
Contributor Author

zach593 commented Jan 8, 2025

BTW, I think we could consider use json.Marshal() instead of all of x.MarshalJSON(). Because json.Marshal() is more general, it will also call x.MarshalJSON() internally, and will also perform some other optimizations.

@XiShanYongYe-Chang
Copy link
Member

Sure, can you help with that? I'm not familiar with commands with /

/remove-kind bug
/kind cleanup

@karmada-bot karmada-bot added kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. and removed kind/bug Categorizes issue or PR as related to a bug. labels Jan 9, 2025
@chaosi-zju
Copy link
Member

As we discussed in zoom, for the performance optimization, this PR is not enough, I may need another 2 PRs to complete this goal. Since I need more than one PR, I created #6017 to track it, and we can have further discussion in that issue.

ok, thanks, I think this PR can move forward first

/lgtm

@karmada-bot karmada-bot added the lgtm Indicates that a PR is ready to be merged. label Jan 9, 2025
Copy link
Member

@RainbowMango RainbowMango left a comment

Choose a reason for hiding this comment

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

/assign
I will take a look this week.

@@ -54,7 +55,7 @@ func CreateOrUpdateWork(ctx context.Context, client client.Client, workMeta meta
}
}

workloadJSON, err := resource.MarshalJSON()
workloadJSON, err := json.Marshal(resource)
Copy link
Member

Choose a reason for hiding this comment

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

Is there any difference between encoding/json.Marshal and unstructured.MarshalJSON other than the /n?
I am mostly curious about functionality and performance.

Copy link
Member

Choose a reason for hiding this comment

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

In addition, I think we need to check all of the places where using the unstructured.MarshalJSON.

PS: I don't mean to check and fix them all in this PR. Just remind me.

Copy link
Member

Choose a reason for hiding this comment

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

Tracked this on #6031, please correct me if anyone feels this is unnecessary.

Copy link
Member

@chaosi-zju chaosi-zju Jan 10, 2025

Choose a reason for hiding this comment

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

Is there any difference between encoding/json.Marshal and unstructured.MarshalJSON other than the /n?
I am mostly curious about functionality and performance.

I did a benchmark test to compare different json marshal function, my test code is as:

benchmark test code
func BenchmarkMarshalJSON(b *testing.B) {
	resource := &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "apps/v1",
			"kind":       "Deployment",
			"metadata": map[string]interface{}{
				"name":      "test-deployment",
				"namespace": "default",
			},
			"spec": map[string]interface{}{
				"replicas": 3,
				"selector": map[string]interface{}{
					"matchLabels": map[string]interface{}{
						"app": "test",
					},
				},
				"template": map[string]interface{}{
					"metadata": map[string]interface{}{
						"labels": map[string]interface{}{
							"app": "test",
						},
					},
					"spec": map[string]interface{}{
						"containers": []interface{}{
							map[string]interface{}{
								"name":  "test-container",
								"image": "nginx:latest",
							},
						},
					},
				},
			},
		},
	}

	b.Run("resource.MarshalJSON", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			_, _ = resource.MarshalJSON()
		}
	})

	b.Run("json.Marshal", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
                       // "encoding/json"
			_, _ = json.Marshal(resource)
		}
	})

	b.Run("jsoniter.Marshal", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
                       // jsoniter "github.com/json-iterator/go"
			_, _ = jsoniter.Marshal(resource)
		}
	})
}

the result is:

goos: windows
goarch: amd64
pkg: github.com/karmada-io/karmada/pkg/controllers/ctrlutil
cpu: Intel(R) Core(TM) i5-14600KF
BenchmarkMarshalJSON
BenchmarkMarshalJSON/resource.MarshalJSON
BenchmarkMarshalJSON/resource.MarshalJSON-20         	  398614	      2835 ns/op
BenchmarkMarshalJSON/json.Marshal
BenchmarkMarshalJSON/json.Marshal-20                 	  307854	      4004 ns/op
BenchmarkMarshalJSON/jsoniter.Marshal
BenchmarkMarshalJSON/jsoniter.Marshal-20             	  393528	      3019 ns/op
PASS

and

goos: windows
goarch: amd64
pkg: github.com/karmada-io/karmada/pkg/util/helper
cpu: Intel(R) Xeon(R) Gold 6278C CPU @ 2.60GHz
BenchmarkMarshalJSON
BenchmarkMarshalJSON/resource.MarshalJSON
BenchmarkMarshalJSON/resource.MarshalJSON-4                95715             13065 ns/op
BenchmarkMarshalJSON/json.Marshal
BenchmarkMarshalJSON/json.Marshal-4                        68590             16391 ns/op
BenchmarkMarshalJSON/jsoniter.Marshal
BenchmarkMarshalJSON/jsoniter.Marshal-4                    79052             13281 ns/op
PASS

seems previous unstructured.MarshalJSON perform better, while encoding/json.Marshal worst

Copy link
Member

Choose a reason for hiding this comment

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

resource.MarshalJSON() is specifically optimized for the unstructured.Unstructured type, potentially offering significant performance improvements when handling large numbers of Kubernetes resources.

besides, Kubernetes resources may contain fields that require special treatment during serialization, which can be handled by the custom method, for example:

  • Timestamp fields like creationTimestamp may need to be formatted in a particular way.
  • Some fields might need to be omitted entirely when they're empty, rather than being serialized as null.
  • Certain fields may require custom serialization rules to maintain backward compatibility with older API versions.
  • Resource-specific fields (like status in various resources) might need special treatment during serialization.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

json.Marshal() also calls x.MarshalJSON() inside because unstructured.Unstructured implemented the json.Marshaler interface by implemented MarshalJSON() function.

@karmada-bot karmada-bot removed the lgtm Indicates that a PR is ready to be merged. label Jan 10, 2025
@karmada-bot
Copy link
Collaborator

New changes are detected. LGTM label has been removed.

@karmada-bot
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please ask for approval from rainbowmango. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. size/XS Denotes a PR that changes 0-9 lines, ignoring generated files.
Projects
None yet
6 participants