Merge pull request #89575 from tnqn/improve-tracker

Improve fake clientset performance
This commit is contained in:
Kubernetes Prow Robot 2020-03-30 08:22:07 -07:00 committed by GitHub
commit c968317ebd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 57 deletions

View File

@ -75,9 +75,9 @@ func TestList(t *testing.T) {
} }
expected := []unstructured.Unstructured{ expected := []unstructured.Unstructured{
*newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
*newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"), *newUnstructured("group/version", "TheKind", "ns-foo", "name-bar"),
*newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"), *newUnstructured("group/version", "TheKind", "ns-foo", "name-baz"),
*newUnstructured("group/version", "TheKind", "ns-foo", "name-foo"),
} }
if !equality.Semantic.DeepEqual(listFirst.Items, expected) { if !equality.Semantic.DeepEqual(listFirst.Items, expected) {
t.Fatal(diff.ObjectGoPrintDiff(expected, listFirst.Items)) t.Fatal(diff.ObjectGoPrintDiff(expected, listFirst.Items))

View File

@ -79,9 +79,9 @@ func TestList(t *testing.T) {
} }
expected := []metav1.PartialObjectMetadata{ expected := []metav1.PartialObjectMetadata{
*newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-foo"),
*newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-bar"), *newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-bar"),
*newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-baz"), *newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-baz"),
*newPartialObjectMetadata("group/version", "TheKind", "ns-foo", "name-foo"),
} }
if !equality.Semantic.DeepEqual(listFirst.Items, expected) { if !equality.Semantic.DeepEqual(listFirst.Items, expected) {
t.Fatal(diff.ObjectGoPrintDiff(expected, listFirst.Items)) t.Fatal(diff.ObjectGoPrintDiff(expected, listFirst.Items))

View File

@ -19,6 +19,7 @@ package testing
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"sort"
"sync" "sync"
jsonpatch "github.com/evanphx/json-patch" jsonpatch "github.com/evanphx/json-patch"
@ -197,7 +198,7 @@ type tracker struct {
scheme ObjectScheme scheme ObjectScheme
decoder runtime.Decoder decoder runtime.Decoder
lock sync.RWMutex lock sync.RWMutex
objects map[schema.GroupVersionResource][]runtime.Object objects map[schema.GroupVersionResource]map[types.NamespacedName]runtime.Object
// The value type of watchers is a map of which the key is either a namespace or // The value type of watchers is a map of which the key is either a namespace or
// all/non namespace aka "" and its value is list of fake watchers. // all/non namespace aka "" and its value is list of fake watchers.
// Manipulations on resources will broadcast the notification events into the // Manipulations on resources will broadcast the notification events into the
@ -214,7 +215,7 @@ func NewObjectTracker(scheme ObjectScheme, decoder runtime.Decoder) ObjectTracke
return &tracker{ return &tracker{
scheme: scheme, scheme: scheme,
decoder: decoder, decoder: decoder,
objects: make(map[schema.GroupVersionResource][]runtime.Object), objects: make(map[schema.GroupVersionResource]map[types.NamespacedName]runtime.Object),
watchers: make(map[schema.GroupVersionResource]map[string][]*watch.RaceFreeFakeWatcher), watchers: make(map[schema.GroupVersionResource]map[string][]*watch.RaceFreeFakeWatcher),
} }
} }
@ -282,31 +283,15 @@ func (t *tracker) Get(gvr schema.GroupVersionResource, ns, name string) (runtime
return nil, errNotFound return nil, errNotFound
} }
var matchingObjs []runtime.Object matchingObj, ok := objs[types.NamespacedName{Namespace: ns, Name: name}]
for _, obj := range objs { if !ok {
acc, err := meta.Accessor(obj)
if err != nil {
return nil, err
}
if acc.GetNamespace() != ns {
continue
}
if acc.GetName() != name {
continue
}
matchingObjs = append(matchingObjs, obj)
}
if len(matchingObjs) == 0 {
return nil, errNotFound return nil, errNotFound
} }
if len(matchingObjs) > 1 {
return nil, fmt.Errorf("more than one object matched gvr %s, ns: %q name: %q", gvr, ns, name)
}
// Only one object should match in the tracker if it works // Only one object should match in the tracker if it works
// correctly, as Add/Update methods enforce kind/namespace/name // correctly, as Add/Update methods enforce kind/namespace/name
// uniqueness. // uniqueness.
obj := matchingObjs[0].DeepCopyObject() obj := matchingObj.DeepCopyObject()
if status, ok := obj.(*metav1.Status); ok { if status, ok := obj.(*metav1.Status); ok {
if status.Status != metav1.StatusSuccess { if status.Status != metav1.StatusSuccess {
return nil, &errors.StatusError{ErrStatus: *status} return nil, &errors.StatusError{ErrStatus: *status}
@ -405,29 +390,29 @@ func (t *tracker) add(gvr schema.GroupVersionResource, obj runtime.Object, ns st
return errors.NewBadRequest(msg) return errors.NewBadRequest(msg)
} }
for i, existingObj := range t.objects[gvr] { _, ok := t.objects[gvr]
oldMeta, err := meta.Accessor(existingObj) if !ok {
if err != nil { t.objects[gvr] = make(map[types.NamespacedName]runtime.Object)
return err
} }
if oldMeta.GetNamespace() == newMeta.GetNamespace() && oldMeta.GetName() == newMeta.GetName() {
namespacedName := types.NamespacedName{Namespace: newMeta.GetNamespace(), Name: newMeta.GetName()}
if _, ok = t.objects[gvr][namespacedName]; ok {
if replaceExisting { if replaceExisting {
for _, w := range t.getWatches(gvr, ns) { for _, w := range t.getWatches(gvr, ns) {
w.Modify(obj) w.Modify(obj)
} }
t.objects[gvr][i] = obj t.objects[gvr][namespacedName] = obj
return nil return nil
} }
return errors.NewAlreadyExists(gr, newMeta.GetName()) return errors.NewAlreadyExists(gr, newMeta.GetName())
} }
}
if replaceExisting { if replaceExisting {
// Tried to update but no matching object was found. // Tried to update but no matching object was found.
return errors.NewNotFound(gr, newMeta.GetName()) return errors.NewNotFound(gr, newMeta.GetName())
} }
t.objects[gvr] = append(t.objects[gvr], obj) t.objects[gvr][namespacedName] = obj
for _, w := range t.getWatches(gvr, ns) { for _, w := range t.getWatches(gvr, ns) {
w.Add(obj) w.Add(obj)
@ -457,35 +442,28 @@ func (t *tracker) Delete(gvr schema.GroupVersionResource, ns, name string) error
t.lock.Lock() t.lock.Lock()
defer t.lock.Unlock() defer t.lock.Unlock()
found := false objs, ok := t.objects[gvr]
if !ok {
for i, existingObj := range t.objects[gvr] { return errors.NewNotFound(gvr.GroupResource(), name)
objMeta, err := meta.Accessor(existingObj)
if err != nil {
return err
} }
if objMeta.GetNamespace() == ns && objMeta.GetName() == name {
obj := t.objects[gvr][i] namespacedName := types.NamespacedName{Namespace: ns, Name: name}
t.objects[gvr] = append(t.objects[gvr][:i], t.objects[gvr][i+1:]...) obj, ok := objs[namespacedName]
if !ok {
return errors.NewNotFound(gvr.GroupResource(), name)
}
delete(objs, namespacedName)
for _, w := range t.getWatches(gvr, ns) { for _, w := range t.getWatches(gvr, ns) {
w.Delete(obj) w.Delete(obj)
} }
found = true
break
}
}
if found {
return nil return nil
} }
return errors.NewNotFound(gvr.GroupResource(), name)
}
// filterByNamespace returns all objects in the collection that // filterByNamespace returns all objects in the collection that
// match provided namespace. Empty namespace matches // match provided namespace. Empty namespace matches
// non-namespaced objects. // non-namespaced objects.
func filterByNamespace(objs []runtime.Object, ns string) ([]runtime.Object, error) { func filterByNamespace(objs map[types.NamespacedName]runtime.Object, ns string) ([]runtime.Object, error) {
var res []runtime.Object var res []runtime.Object
for _, obj := range objs { for _, obj := range objs {
@ -499,6 +477,15 @@ func filterByNamespace(objs []runtime.Object, ns string) ([]runtime.Object, erro
res = append(res, obj) res = append(res, obj)
} }
// Sort res to get deterministic order.
sort.Slice(res, func(i, j int) bool {
acc1, _ := meta.Accessor(res[i])
acc2, _ := meta.Accessor(res[j])
if acc1.GetNamespace() != acc2.GetNamespace() {
return acc1.GetNamespace() < acc2.GetNamespace()
}
return acc1.GetName() < acc2.GetName()
})
return res, nil return res, nil
} }