From bd8e39e3f9828cc43b8954b7c2ffb09a0288b1b4 Mon Sep 17 00:00:00 2001 From: Sargun Dhillon Date: Sat, 27 Jul 2019 12:43:39 -0700 Subject: [PATCH] Add a benchmark for pod creation This adds a benchmark for pod creation and makes the mock_test provider actually work correctly in concurrent situations. --- go.mod | 1 + go.sum | 4 ++++ node/lifecycle_test.go | 52 ++++++++++++++++++++++++++++++++++++++++-- node/mock_test.go | 23 ++++++++++--------- 4 files changed, 67 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 9cd8eaa18..e72c2efb5 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( github.com/modern-go/reflect2 v1.0.1 // indirect github.com/onsi/ginkgo v1.8.0 // indirect github.com/onsi/gomega v1.5.0 // indirect + github.com/pborman/uuid v1.2.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.8.1 github.com/sirupsen/logrus v1.4.1 diff --git a/go.sum b/go.sum index 14f2a0d0a..60cebcfb4 100644 --- a/go.sum +++ b/go.sum @@ -68,6 +68,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI= github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= @@ -125,6 +127,8 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= diff --git a/node/lifecycle_test.go b/node/lifecycle_test.go index 4b9f73974..059f3d571 100644 --- a/node/lifecycle_test.go +++ b/node/lifecycle_test.go @@ -15,6 +15,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/watch" kubeinformers "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" @@ -414,8 +415,51 @@ func testCreateStartDeleteScenario(ctx context.Context, t *testing.T, s *system) } } -func newPod() *corev1.Pod { - return &corev1.Pod{ +func BenchmarkCreatePods(b *testing.B) { + sl := logrus.StandardLogger() + sl.SetLevel(logrus.ErrorLevel) + newLogger := logruslogger.FromLogrus(logrus.NewEntry(sl)) + + ctx := context.Background() + ctx = log.WithLogger(ctx, newLogger) + + assert.NilError(b, wireUpSystem(ctx, newMockProvider(), func(ctx context.Context, s *system) { + benchmarkCreatePods(ctx, b, s) + })) +} + +func benchmarkCreatePods(ctx context.Context, b *testing.B, s *system) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + errCh := s.start(ctx) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + pod := newPod(randomizeUID, randomizeName) + _, err := s.client.CoreV1().Pods(pod.Namespace).Create(pod) + assert.NilError(b, err) + select { + case err = <-errCh: + b.Fatalf("Benchmark terminated with error: %+v", err) + default: + } + } +} + +type podModifier func(*corev1.Pod) + +func randomizeUID(pod *corev1.Pod) { + pod.ObjectMeta.UID = uuid.NewUUID() +} + +func randomizeName(pod *corev1.Pod) { + name := fmt.Sprintf("pod-%s", uuid.NewUUID()) + pod.Name = name +} + +func newPod(podmodifiers ...podModifier) *corev1.Pod { + pod := &corev1.Pod{ TypeMeta: metav1.TypeMeta{ Kind: "Pod", APIVersion: "v1", @@ -433,4 +477,8 @@ func newPod() *corev1.Pod { Phase: corev1.PodPending, }, } + for _, modifier := range podmodifiers { + modifier(pod) + } + return pod } diff --git a/node/mock_test.go b/node/mock_test.go index 62c713c80..1ab237dd2 100644 --- a/node/mock_test.go +++ b/node/mock_test.go @@ -3,6 +3,7 @@ package node import ( "context" "fmt" + "sync" "time" "github.com/virtual-kubelet/virtual-kubelet/errdefs" @@ -23,7 +24,7 @@ type mockV0Provider struct { errorOnDelete error - pods map[string]*v1.Pod + pods sync.Map startTime time.Time notifier func(*v1.Pod) } @@ -35,7 +36,6 @@ type mockProvider struct { // NewMockProviderMockConfig creates a new mockV0Provider. Mock legacy provider does not implement the new asynchronous podnotifier interface func newMockV0Provider() *mockV0Provider { provider := mockV0Provider{ - pods: make(map[string]*v1.Pod), startTime: time.Now(), // By default notifier is set to a function which is a no-op. In the event we've implemented the PodNotifier interface, // it will be set, and then we'll call a real underlying implementation. @@ -98,7 +98,7 @@ func (p *mockV0Provider) CreatePod(ctx context.Context, pod *v1.Pod) error { }) } - p.pods[key] = pod + p.pods.Store(key, pod) p.notifier(pod) return nil @@ -114,7 +114,7 @@ func (p *mockV0Provider) UpdatePod(ctx context.Context, pod *v1.Pod) error { return err } - p.pods[key] = pod + p.pods.Store(key, pod) p.notifier(pod) return nil @@ -134,12 +134,12 @@ func (p *mockV0Provider) DeletePod(ctx context.Context, pod *v1.Pod) (err error) return err } - if _, exists := p.pods[key]; !exists { + if _, exists := p.pods.Load(key); !exists { return errdefs.NotFound("pod not found") } now := metav1.Now() - delete(p.pods, key) + p.pods.Delete(key) pod.Status.Phase = v1.PodSucceeded pod.Status.Reason = "MockProviderPodDeleted" @@ -169,8 +169,8 @@ func (p *mockV0Provider) GetPod(ctx context.Context, namespace, name string) (po return nil, err } - if pod, ok := p.pods[key]; ok { - return pod, nil + if pod, ok := p.pods.Load(key); ok { + return pod.(*v1.Pod), nil } return nil, errdefs.NotFoundf("pod \"%s/%s\" is not known to the provider", namespace, name) } @@ -194,9 +194,10 @@ func (p *mockV0Provider) GetPods(ctx context.Context) ([]*v1.Pod, error) { var pods []*v1.Pod - for _, pod := range p.pods { - pods = append(pods, pod) - } + p.pods.Range(func(key, pod interface{}) bool { + pods = append(pods, pod.(*v1.Pod)) + return true + }) return pods, nil }