mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-11 13:02:14 +00:00
Merge pull request #125884 from serathius/benchmark-storage
Benchmark storage
This commit is contained in:
commit
3a84906904
@ -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=
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user