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-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
|
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 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/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc=
|
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=
|
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 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
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 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.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
|
||||||
github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
|
github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
|
||||||
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
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-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE=
|
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 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/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.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
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-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
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 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/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/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=
|
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/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
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 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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
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/coreos/go-systemd/v22 v22.5.0
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0
|
github.com/emicklei/go-restful/v3 v3.11.0
|
||||||
github.com/fsnotify/fsnotify v1.7.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/gogo/protobuf v1.3.2
|
||||||
github.com/google/cel-go v0.20.1
|
github.com/google/cel-go v0.20.1
|
||||||
github.com/google/gnostic-models v0.6.8
|
github.com/google/gnostic-models v0.6.8
|
||||||
@ -70,7 +71,6 @@ require (
|
|||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 // 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-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
|
@ -22,6 +22,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
@ -37,7 +39,9 @@ import (
|
|||||||
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
|
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
|
||||||
storagetesting "k8s.io/apiserver/pkg/storage/testing"
|
storagetesting "k8s.io/apiserver/pkg/storage/testing"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
"k8s.io/utils/clock"
|
"k8s.io/utils/clock"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -396,6 +400,7 @@ type setupOptions struct {
|
|||||||
resourcePrefix string
|
resourcePrefix string
|
||||||
keyFunc func(runtime.Object) (string, error)
|
keyFunc func(runtime.Object) (string, error)
|
||||||
indexerFuncs map[string]storage.IndexerFunc
|
indexerFuncs map[string]storage.IndexerFunc
|
||||||
|
indexers cache.Indexers
|
||||||
clock clock.WithTicker
|
clock clock.WithTicker
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,6 +430,12 @@ func withSpecNodeNameIndexerFuncs(options *setupOptions) {
|
|||||||
return pod.Spec.NodeName
|
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) {
|
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
|
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{}
|
setupOpts := setupOptions{}
|
||||||
opts = append([]setupOption{withDefaults}, opts...)
|
opts = append([]setupOption{withDefaults}, opts...)
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
@ -456,6 +467,7 @@ func testSetupWithEtcdServer(t *testing.T, opts ...setupOption) (context.Context
|
|||||||
NewFunc: newPod,
|
NewFunc: newPod,
|
||||||
NewListFunc: newPodList,
|
NewListFunc: newPodList,
|
||||||
IndexerFuncs: setupOpts.indexerFuncs,
|
IndexerFuncs: setupOpts.indexerFuncs,
|
||||||
|
Indexers: &setupOpts.indexers,
|
||||||
Codec: codecs.LegacyCodec(examplev1.SchemeGroupVersion),
|
Codec: codecs.LegacyCodec(examplev1.SchemeGroupVersion),
|
||||||
Clock: setupOpts.clock,
|
Clock: setupOpts.clock,
|
||||||
}
|
}
|
||||||
@ -521,3 +533,24 @@ func (c *createWrapper) Create(ctx context.Context, key string, obj, out runtime
|
|||||||
return true, nil
|
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 newPod() runtime.Object { return &example.Pod{} }
|
||||||
func newPodList() runtime.Object { return &example.PodList{} }
|
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)
|
server, _ := etcd3testing.NewUnsecuredEtcd3TestClientServer(t)
|
||||||
storage := etcd3.New(
|
storage := etcd3.New(
|
||||||
server.V3Client,
|
server.V3Client,
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
clientv3 "go.etcd.io/etcd/client/v3"
|
clientv3 "go.etcd.io/etcd/client/v3"
|
||||||
"go.etcd.io/etcd/server/v3/embed"
|
"go.etcd.io/etcd/server/v3/embed"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
@ -45,6 +46,7 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/storage/etcd3/testserver"
|
"k8s.io/apiserver/pkg/storage/etcd3/testserver"
|
||||||
storagetesting "k8s.io/apiserver/pkg/storage/testing"
|
storagetesting "k8s.io/apiserver/pkg/storage/testing"
|
||||||
"k8s.io/apiserver/pkg/storage/value"
|
"k8s.io/apiserver/pkg/storage/value"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var scheme = runtime.NewScheme()
|
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
|
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
|
// no-op, server termination moved to test cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUnsecuredEtcd3TestClientServer creates a new client and server for testing
|
// 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 := &EtcdTestServer{}
|
||||||
server.V3Client = testserver.RunEtcd(t, nil)
|
server.V3Client = testserver.RunEtcd(t, nil)
|
||||||
config := &storagebackend.Config{
|
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