diff --git a/go.work.sum b/go.work.sum index 87d235a0eaa..5d0a2a487f1 100644 --- a/go.work.sum +++ b/go.work.sum @@ -379,6 +379,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IU github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/aws/aws-sdk-go v1.35.24 h1:U3GNTg8+7xSM6OAJ8zksiSM4bRqxBWmVwwehvOSNG3A= +github.com/aws/aws-sdk-go v1.35.24/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= @@ -432,6 +433,7 @@ github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= @@ -491,6 +493,7 @@ github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= @@ -515,6 +518,7 @@ github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzR github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= +github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= @@ -525,6 +529,7 @@ github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= diff --git a/staging/src/k8s.io/apiserver/go.mod b/staging/src/k8s.io/apiserver/go.mod index 7310281dd2f..1e405a96d19 100644 --- a/staging/src/k8s.io/apiserver/go.mod +++ b/staging/src/k8s.io/apiserver/go.mod @@ -10,6 +10,7 @@ require ( github.com/coreos/go-systemd/v22 v22.5.0 github.com/emicklei/go-restful/v3 v3.11.0 github.com/fsnotify/fsnotify v1.7.0 + github.com/go-logr/logr v1.4.2 github.com/gogo/protobuf v1.3.2 github.com/google/cel-go v0.20.1 github.com/google/gnostic-models v0.6.8 @@ -70,7 +71,6 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect diff --git a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_test.go b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_test.go index 38ea9f0341f..013fcf81e3b 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_test.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_test.go @@ -22,6 +22,8 @@ import ( "testing" "time" + "github.com/go-logr/logr" + apiequality "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -37,7 +39,9 @@ import ( etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" storagetesting "k8s.io/apiserver/pkg/storage/testing" utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/client-go/tools/cache" featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/klog/v2" "k8s.io/utils/clock" ) @@ -396,6 +400,7 @@ type setupOptions struct { resourcePrefix string keyFunc func(runtime.Object) (string, error) indexerFuncs map[string]storage.IndexerFunc + indexers cache.Indexers clock clock.WithTicker } @@ -425,6 +430,12 @@ func withSpecNodeNameIndexerFuncs(options *setupOptions) { return pod.Spec.NodeName }, } + options.indexers = map[string]cache.IndexFunc{ + "f:spec.nodeName": func(obj interface{}) ([]string, error) { + pod := obj.(*example.Pod) + return []string{pod.Spec.NodeName}, nil + }, + } } func testSetup(t *testing.T, opts ...setupOption) (context.Context, *Cacher, tearDownFunc) { @@ -432,7 +443,7 @@ func testSetup(t *testing.T, opts ...setupOption) (context.Context, *Cacher, tea return ctx, cacher, tearDown } -func testSetupWithEtcdServer(t *testing.T, opts ...setupOption) (context.Context, *Cacher, *etcd3testing.EtcdTestServer, tearDownFunc) { +func testSetupWithEtcdServer(t testing.TB, opts ...setupOption) (context.Context, *Cacher, *etcd3testing.EtcdTestServer, tearDownFunc) { setupOpts := setupOptions{} opts = append([]setupOption{withDefaults}, opts...) for _, opt := range opts { @@ -456,6 +467,7 @@ func testSetupWithEtcdServer(t *testing.T, opts ...setupOption) (context.Context NewFunc: newPod, NewListFunc: newPodList, IndexerFuncs: setupOpts.indexerFuncs, + Indexers: &setupOpts.indexers, Codec: codecs.LegacyCodec(examplev1.SchemeGroupVersion), Clock: setupOpts.clock, } @@ -521,3 +533,24 @@ func (c *createWrapper) Create(ctx context.Context, key string, obj, out runtime return true, nil }) } + +func BenchmarkStoreListCreate(b *testing.B) { + klog.SetLogger(logr.Discard()) + b.Run("RV=NotOlderThan", func(b *testing.B) { + ctx, cacher, _, terminate := testSetupWithEtcdServer(b) + b.Cleanup(terminate) + storagetesting.RunBenchmarkStoreListCreate(ctx, b, cacher, metav1.ResourceVersionMatchNotOlderThan) + }) + b.Run("RV=ExactMatch", func(b *testing.B) { + ctx, cacher, _, terminate := testSetupWithEtcdServer(b) + b.Cleanup(terminate) + storagetesting.RunBenchmarkStoreListCreate(ctx, b, cacher, metav1.ResourceVersionMatchExact) + }) +} + +func BenchmarkStoreList(b *testing.B) { + klog.SetLogger(logr.Discard()) + ctx, cacher, _, terminate := testSetupWithEtcdServer(b, withSpecNodeNameIndexerFuncs) + b.Cleanup(terminate) + storagetesting.RunBenchmarkStoreList(ctx, b, cacher) +} diff --git a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_testing_utils_test.go b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_testing_utils_test.go index 4fe2e759ae7..4f1fed104e6 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_testing_utils_test.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_testing_utils_test.go @@ -55,7 +55,7 @@ func init() { func newPod() runtime.Object { return &example.Pod{} } func newPodList() runtime.Object { return &example.PodList{} } -func newEtcdTestStorage(t *testing.T, prefix string) (*etcd3testing.EtcdTestServer, storage.Interface) { +func newEtcdTestStorage(t testing.TB, prefix string) (*etcd3testing.EtcdTestServer, storage.Interface) { server, _ := etcd3testing.NewUnsecuredEtcd3TestClientServer(t) storage := etcd3.New( server.V3Client, diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go index 8a84e5569e8..af5b52c2ea6 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go @@ -27,6 +27,7 @@ import ( "sync/atomic" "testing" + "github.com/go-logr/logr" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/server/v3/embed" "google.golang.org/grpc/grpclog" @@ -45,6 +46,7 @@ import ( "k8s.io/apiserver/pkg/storage/etcd3/testserver" storagetesting "k8s.io/apiserver/pkg/storage/testing" "k8s.io/apiserver/pkg/storage/value" + "k8s.io/klog/v2" ) var scheme = runtime.NewScheme() @@ -905,3 +907,20 @@ func BenchmarkStore_GetList(b *testing.B) { }) } } + +func BenchmarkStoreListCreate(b *testing.B) { + klog.SetLogger(logr.Discard()) + b.Run("RV=NotOlderThan", func(b *testing.B) { + ctx, store, _ := testSetup(b) + storagetesting.RunBenchmarkStoreListCreate(ctx, b, store, metav1.ResourceVersionMatchNotOlderThan) + }) + b.Run("RV=ExactMatch", func(b *testing.B) { + ctx, store, _ := testSetup(b) + storagetesting.RunBenchmarkStoreListCreate(ctx, b, store, metav1.ResourceVersionMatchExact) + }) +} + +func BenchmarkStoreList(b *testing.B) { + ctx, store, _ := testSetup(b) + storagetesting.RunBenchmarkStoreList(ctx, b, store) +} diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/testing/test_server.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/testing/test_server.go index 235cb78ca51..c5b7ad0feea 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/testing/test_server.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/testing/test_server.go @@ -30,12 +30,12 @@ type EtcdTestServer struct { V3Client *clientv3.Client } -func (e *EtcdTestServer) Terminate(t *testing.T) { +func (e *EtcdTestServer) Terminate(t testing.TB) { // no-op, server termination moved to test cleanup } // NewUnsecuredEtcd3TestClientServer creates a new client and server for testing -func NewUnsecuredEtcd3TestClientServer(t *testing.T) (*EtcdTestServer, *storagebackend.Config) { +func NewUnsecuredEtcd3TestClientServer(t testing.TB) (*EtcdTestServer, *storagebackend.Config) { server := &EtcdTestServer{} server.V3Client = testserver.RunEtcd(t, nil) config := &storagebackend.Config{ diff --git a/staging/src/k8s.io/apiserver/pkg/storage/testing/store_benchmarks.go b/staging/src/k8s.io/apiserver/pkg/storage/testing/store_benchmarks.go new file mode 100644 index 00000000000..c5ad2c6a853 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/storage/testing/store_benchmarks.go @@ -0,0 +1,261 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testing + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/apiserver/pkg/apis/example" + "k8s.io/apiserver/pkg/storage" +) + +func RunBenchmarkStoreListCreate(ctx context.Context, b *testing.B, store storage.Interface, match metav1.ResourceVersionMatch) { + objectCount := atomic.Uint64{} + pods := []*example.Pod{} + for i := 0; i < b.N; i++ { + name := rand.String(100) + pods = append(pods, &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: name}}) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + pod := pods[i] + podOut := &example.Pod{} + err := store.Create(ctx, computePodKey(pod), pod, podOut, 0) + if err != nil { + panic(fmt.Sprintf("Unexpected error %s", err)) + } + listOut := &example.PodList{} + err = store.GetList(ctx, "/pods", storage.ListOptions{ + Recursive: true, + ResourceVersion: podOut.ResourceVersion, + ResourceVersionMatch: match, + Predicate: storage.SelectionPredicate{ + Label: labels.Everything(), + Field: fields.Everything(), + Limit: 1, + }, + }, listOut) + if err != nil { + panic(fmt.Sprintf("Unexpected error %s", err)) + } + if len(listOut.Items) != 1 { + b.Errorf("Expected to get 1 element, got %d", len(listOut.Items)) + } + objectCount.Add(uint64(len(listOut.Items))) + } + b.ReportMetric(float64(objectCount.Load())/float64(b.N), "objects/op") +} + +func RunBenchmarkStoreList(ctx context.Context, b *testing.B, store storage.Interface) { + namespaceCount := 100 + podPerNamespaceCount := 100 + var paginateLimit int64 = 100 + nodeCount := 100 + namespacedNames, nodeNames := prepareBenchchmarkData(ctx, store, namespaceCount, podPerNamespaceCount, nodeCount) + b.ResetTimer() + maxRevision := 1 + namespaceCount*podPerNamespaceCount + cases := []struct { + name string + match metav1.ResourceVersionMatch + }{ + { + name: "RV=Empty", + match: "", + }, + { + name: "RV=NotOlderThan", + match: metav1.ResourceVersionMatchNotOlderThan, + }, + { + name: "RV=MatchExact", + match: metav1.ResourceVersionMatchExact, + }, + } + + for _, c := range cases { + b.Run(c.name, func(b *testing.B) { + runBenchmarkStoreList(ctx, b, store, 0, maxRevision, c.match, false, nodeNames) + }) + } + b.Run("Paginate", func(b *testing.B) { + for _, c := range cases { + b.Run(c.name, func(b *testing.B) { + runBenchmarkStoreList(ctx, b, store, paginateLimit, maxRevision, c.match, false, nodeNames) + }) + } + }) + b.Run("NodeIndexed", func(b *testing.B) { + for _, c := range cases { + b.Run(c.name, func(b *testing.B) { + runBenchmarkStoreList(ctx, b, store, 0, maxRevision, c.match, true, nodeNames) + }) + } + b.Run("Paginate", func(b *testing.B) { + for _, c := range cases { + b.Run(c.name, func(b *testing.B) { + runBenchmarkStoreList(ctx, b, store, paginateLimit, maxRevision, c.match, true, nodeNames) + }) + } + }) + }) + b.Run("Namespace", func(b *testing.B) { + for _, c := range cases { + b.Run(c.name, func(b *testing.B) { + runBenchmarkStoreListNamespace(ctx, b, store, maxRevision, c.match, namespacedNames) + }) + } + }) +} + +func runBenchmarkStoreListNamespace(ctx context.Context, b *testing.B, store storage.Interface, maxRV int, match metav1.ResourceVersionMatch, namespaceNames []string) { + wg := sync.WaitGroup{} + objectCount := atomic.Uint64{} + pageCount := atomic.Uint64{} + b.ResetTimer() + for i := 0; i < b.N; i++ { + wg.Add(1) + resourceVersion := "" + switch match { + case metav1.ResourceVersionMatchExact, metav1.ResourceVersionMatchNotOlderThan: + resourceVersion = fmt.Sprintf("%d", maxRV-99+i%100) + } + go func(resourceVersion string) { + defer wg.Done() + opts := storage.ListOptions{ + Recursive: true, + ResourceVersion: resourceVersion, + ResourceVersionMatch: match, + Predicate: storage.Everything, + } + for j := 0; j < len(namespaceNames); j++ { + objects, pages := paginate(ctx, store, "/pods/"+namespaceNames[j], opts) + objectCount.Add(uint64(objects)) + pageCount.Add(uint64(pages)) + } + }(resourceVersion) + } + wg.Wait() + b.ReportMetric(float64(objectCount.Load())/float64(b.N), "objects/op") + b.ReportMetric(float64(pageCount.Load())/float64(b.N), "pages/op") +} + +func runBenchmarkStoreList(ctx context.Context, b *testing.B, store storage.Interface, limit int64, maxRV int, match metav1.ResourceVersionMatch, perNode bool, nodeNames []string) { + wg := sync.WaitGroup{} + objectCount := atomic.Uint64{} + pageCount := atomic.Uint64{} + for i := 0; i < b.N; i++ { + wg.Add(1) + resourceVersion := "" + switch match { + case metav1.ResourceVersionMatchExact, metav1.ResourceVersionMatchNotOlderThan: + resourceVersion = fmt.Sprintf("%d", maxRV-99+i%100) + } + go func(resourceVersion string) { + defer wg.Done() + opts := storage.ListOptions{ + Recursive: true, + ResourceVersion: resourceVersion, + ResourceVersionMatch: match, + Predicate: storage.SelectionPredicate{ + GetAttrs: podAttr, + Label: labels.Everything(), + Field: fields.Everything(), + Limit: limit, + }, + } + if perNode { + for _, nodeName := range nodeNames { + opts.Predicate.GetAttrs = podAttr + opts.Predicate.IndexFields = []string{"spec.nodeName"} + opts.Predicate.Field = fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeName}) + objects, pages := paginate(ctx, store, "/pods/", opts) + objectCount.Add(uint64(objects)) + pageCount.Add(uint64(pages)) + } + } else { + objects, pages := paginate(ctx, store, "/pods/", opts) + objectCount.Add(uint64(objects)) + pageCount.Add(uint64(pages)) + } + }(resourceVersion) + } + wg.Wait() + b.ReportMetric(float64(objectCount.Load())/float64(b.N), "objects/op") + b.ReportMetric(float64(pageCount.Load())/float64(b.N), "pages/op") +} + +func paginate(ctx context.Context, store storage.Interface, key string, opts storage.ListOptions) (objectCount int, pageCount int) { + listOut := &example.PodList{} + err := store.GetList(ctx, key, opts, listOut) + if err != nil { + panic(fmt.Sprintf("Unexpected error %s", err)) + } + opts.Predicate.Continue = listOut.Continue + opts.ResourceVersion = "" + opts.ResourceVersionMatch = "" + pageCount += 1 + objectCount += len(listOut.Items) + for opts.Predicate.Continue != "" { + listOut := &example.PodList{} + err := store.GetList(ctx, key, opts, listOut) + if err != nil { + panic(fmt.Sprintf("Unexpected error %s", err)) + } + opts.Predicate.Continue = listOut.Continue + pageCount += 1 + objectCount += len(listOut.Items) + } + return objectCount, pageCount +} + +func podAttr(obj runtime.Object) (labels.Set, fields.Set, error) { + pod := obj.(*example.Pod) + return nil, fields.Set{ + "spec.nodeName": pod.Spec.NodeName, + }, nil +} + +func prepareBenchchmarkData(ctx context.Context, store storage.Interface, namespaceCount, podPerNamespaceCount, nodeCount int) (namespaceNames, nodeNames []string) { + nodeNames = make([]string, nodeCount) + for i := 0; i < nodeCount; i++ { + nodeNames[i] = rand.String(100) + } + namespaceNames = make([]string, nodeCount) + out := &example.Pod{} + for i := 0; i < namespaceCount; i++ { + namespace := rand.String(100) + namespaceNames[i] = namespace + for j := 0; j < podPerNamespaceCount; j++ { + name := rand.String(100) + pod := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: name}, Spec: example.PodSpec{NodeName: nodeNames[rand.Intn(nodeCount)]}} + err := store.Create(ctx, computePodKey(pod), pod, out, 0) + if err != nil { + panic(fmt.Sprintf("Unexpected error %s", err)) + } + } + } + return namespaceNames, nodeNames +}