-
Notifications
You must be signed in to change notification settings - Fork 885
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
Performance improve for karmada-controller-manager #2034
Conversation
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: 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 |
9079179
to
9477ac5
Compare
4817c1e
to
7f00732
Compare
n.Lock() | ||
defer n.Unlock() | ||
|
||
totalNum := len(n.clusterSummary[clusterName].nodeSummaryMap) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it safe to call read from map? Will it panic without clusterName
key?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing out.
runtimeObj, err := helper.GetObjectFromCache(c.RESTMapper, c.InformerManager, fedKey) | ||
if err != nil { | ||
if apierrors.IsNotFound(err) { | ||
c.ClusterSummaryCache.delete(fedKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NotFound error shall be returned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
miss return statement
func (c *ClusterStatusController) genHandlerUpdateFunc(clusterName string) func(oldObj, newObj interface{}) { | ||
return func(oldObj, newObj interface{}) { | ||
curObj := newObj.(runtime.Object) | ||
if !reflect.DeepEqual(oldObj, newObj) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you think about comparing with resource version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's ok to comparing with obj, because we don't know the type of obj. If comparing with resource version, may add the cost of converting type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I test it on my side with go 1.17.5. It tells type assert is more faster than reflect.DeepEqual
var (
obj1 interface{}
obj2 interface{}
)
func init() {
pod1Yaml := `<skipped>`
pod2Yaml := `<skipped>`
unstructuredObj1 := &unstructured.Unstructured{}
unstructuredObj2 := &unstructured.Unstructured{}
unstructured.UnstructuredJSONScheme.Decode([]byte(pod1Yaml), nil, unstructuredObj1)
unstructured.UnstructuredJSONScheme.Decode([]byte(pod2Yaml), nil, unstructuredObj2)
obj1, obj2 = unstructuredObj1, unstructuredObj2
}
func BenchmarkDeepEqual(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = reflect.DeepEqual(obj1, obj2)
}
}
func BenchmarkAssert(b *testing.B) {
for i := 0; i < b.N; i++ {
o1, _ := meta.Accessor(obj1)
o2, _ := meta.Accessor(obj2)
_ = o1.GetResourceVersion() == o2.GetResourceVersion()
}
}
goos: darwin
goarch: arm64
pkg: github.com/ikaven1024/gists/k8s/benchmark
BenchmarkDeepEqual
BenchmarkDeepEqual-8 7717909 145.2 ns/op
BenchmarkAssert
BenchmarkAssert-8 33085802 36.39 ns/op
PASS
1000df9
to
8dcdbf6
Compare
Signed-off-by: Poor12 <shentiecheng@huawei.com>
@@ -254,9 +264,9 @@ func (c *ClusterStatusController) buildInformerForCluster(cluster *clusterv1alph | |||
// create the informer for pods and nodes | |||
allSynced := true | |||
for _, gvr := range gvrs { | |||
if !singleClusterInformerManager.IsInformerSynced(gvr) { | |||
if !singleClusterInformerManager.IsInformerSynced(gvr) || !singleClusterInformerManager.IsHandlerExist(gvr, c.getEventHandler(cluster.Name)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Informer synced for gvr
doesn't mean the eventHandler
exit.
Since all controllers share the MultiClusterInformerManager
, the informer might be created(and synced from another place).
if oldMetaInfo.GetResourceVersion() != newMetaInfo.GetResourceVersion() { | ||
return true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks weird that .metadata.ResourceVersion
is not changed in an update event.
I doubt it.
Signed-off-by: Poor12 shentiecheng@huawei.com
What type of PR is this?
/kind feature
What this PR does / why we need it:
As is talked in #1858,when member cluster is large(3k nodes, 10w pods), karmada-controller-manager is easy to be OOMkilled and retry to sync nodes and pods again and again.
Based on #2008, I tested the two clusters with 100 nodes and almost 300 pods. We can easily see that in cluster status controllers, list nodes and pods periodicly cost a lot when the cluster is large(a lot of unmarshal works) according to the frame graph.
So I modified the mechanism from full amount list to
watch based on event
to make the memory increase more stable.Which issue(s) this PR fixes:
Fixes #
Special notes for your reviewer:
Does this PR introduce a user-facing change?:
None