Filter List in Storage level to avoid additional copies.
This commit is contained in:
@@ -150,6 +150,7 @@ func (e *Etcd) List(ctx api.Context, label labels.Selector, field fields.Selecto
|
||||
func (e *Etcd) ListPredicate(ctx api.Context, m generic.Matcher) (runtime.Object, error) {
|
||||
list := e.NewListFunc()
|
||||
trace := util.NewTrace("List " + reflect.TypeOf(list).String())
|
||||
filterFunc := e.filterAndDecorateFunction(m)
|
||||
defer trace.LogIfLong(600 * time.Millisecond)
|
||||
if name, ok := m.MatchesSingle(); ok {
|
||||
trace.Step("About to read single object")
|
||||
@@ -157,21 +158,20 @@ func (e *Etcd) ListPredicate(ctx api.Context, m generic.Matcher) (runtime.Object
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = e.Storage.GetToList(key, list)
|
||||
err = e.Storage.GetToList(key, filterFunc, list)
|
||||
trace.Step("Object extracted")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
trace.Step("About to list directory")
|
||||
err := e.Storage.List(e.KeyRootFunc(ctx), list)
|
||||
err := e.Storage.List(e.KeyRootFunc(ctx), filterFunc, list)
|
||||
trace.Step("List extracted")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer trace.Step("List filtered")
|
||||
return generic.FilterList(list, m, generic.DecoratorFunc(e.Decorator))
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Create inserts a new item according to the unique key from the object.
|
||||
@@ -449,8 +449,21 @@ func (e *Etcd) WatchPredicate(ctx api.Context, m generic.Matcher, resourceVersio
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filterFunc := e.filterAndDecorateFunction(m)
|
||||
|
||||
filterFunc := func(obj runtime.Object) bool {
|
||||
if name, ok := m.MatchesSingle(); ok {
|
||||
key, err := e.KeyFunc(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e.Storage.Watch(key, version, filterFunc)
|
||||
}
|
||||
|
||||
return e.Storage.WatchList(e.KeyRootFunc(ctx), version, filterFunc)
|
||||
}
|
||||
|
||||
func (e *Etcd) filterAndDecorateFunction(m generic.Matcher) func(runtime.Object) bool {
|
||||
return func(obj runtime.Object) bool {
|
||||
matches, err := m.Matches(obj)
|
||||
if err != nil {
|
||||
glog.Errorf("unable to match watch: %v", err)
|
||||
@@ -464,16 +477,6 @@ func (e *Etcd) WatchPredicate(ctx api.Context, m generic.Matcher, resourceVersio
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
if name, ok := m.MatchesSingle(); ok {
|
||||
key, err := e.KeyFunc(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e.Storage.Watch(key, version, filterFunc)
|
||||
}
|
||||
|
||||
return e.Storage.WatchList(e.KeyRootFunc(ctx), version, filterFunc)
|
||||
}
|
||||
|
||||
// calculateTTL is a helper for retrieving the updated TTL for an object or returning an error
|
||||
|
||||
@@ -118,38 +118,3 @@ var (
|
||||
_ = Matcher(&SelectionPredicate{})
|
||||
_ = Matcher(matcherFunc(nil))
|
||||
)
|
||||
|
||||
// DecoratorFunc can mutate the provided object prior to being returned.
|
||||
type DecoratorFunc func(obj runtime.Object) error
|
||||
|
||||
// FilterList filters any list object that conforms to the api conventions,
|
||||
// provided that 'm' works with the concrete type of list. d is an optional
|
||||
// decorator for the returned functions. Only matching items are decorated.
|
||||
func FilterList(list runtime.Object, m Matcher, d DecoratorFunc) (filtered runtime.Object, err error) {
|
||||
// TODO: push a matcher down into tools.etcdHelper to avoid all this
|
||||
// nonsense. This is a lot of unnecessary copies.
|
||||
items, err := runtime.ExtractList(list)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var filteredItems []runtime.Object
|
||||
for _, obj := range items {
|
||||
match, err := m.Matches(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if match {
|
||||
if d != nil {
|
||||
if err := d(obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
filteredItems = append(filteredItems, obj)
|
||||
}
|
||||
}
|
||||
err = runtime.SetList(list, filteredItems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package generic
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
@@ -118,44 +117,6 @@ func TestSelectionPredicate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterList(t *testing.T) {
|
||||
try := &IgnoredList{
|
||||
Items: []Ignored{
|
||||
{"foo"},
|
||||
{"bar"},
|
||||
{"baz"},
|
||||
{"qux"},
|
||||
{"zot"},
|
||||
},
|
||||
}
|
||||
expect := &IgnoredList{
|
||||
Items: []Ignored{
|
||||
{"bar"},
|
||||
{"baz"},
|
||||
},
|
||||
}
|
||||
|
||||
m := MatcherFunc(func(obj runtime.Object) (bool, error) {
|
||||
i, ok := obj.(*Ignored)
|
||||
if !ok {
|
||||
return false, errors.New("wrong type")
|
||||
}
|
||||
return i.ID[0] == 'b', nil
|
||||
})
|
||||
if _, matchesSingleObject := m.MatchesSingle(); matchesSingleObject {
|
||||
t.Errorf("matcher unexpectedly matches only a single object.")
|
||||
}
|
||||
|
||||
got, err := FilterList(try, m, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error %v", err)
|
||||
}
|
||||
|
||||
if e, a := expect, got; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("Expected %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSingleMatch(t *testing.T) {
|
||||
m := MatchOnKey("pod-name-here", func(obj runtime.Object) (bool, error) { return true, nil })
|
||||
got, ok := m.MatchesSingle()
|
||||
|
||||
Reference in New Issue
Block a user