Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance Test changes for EE #8453

Merged
merged 19 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
changelog:
- type: NON_USER_FACING
issueLink: https://github.com/solo-io/solo-projects/issues/5038
resolvesIssue: false
description: >-
Add WithHealthChecks method to UpstreamBuilder and export other performance test helpers for use in EE

skipCI-kube-tests:true
skipCI-docs-build:true
63 changes: 27 additions & 36 deletions projects/gloo/pkg/translator/performance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package translator_test
import (
"context"
"fmt"
"strings"

"github.com/solo-io/gloo/test/ginkgo/decorators"
"go.uber.org/zap"

"github.com/solo-io/go-utils/contextutils"

Expand Down Expand Up @@ -39,14 +39,6 @@ import (
"github.com/solo-io/gloo/projects/gloo/pkg/plugins"
)

// benchmarkConfig allows configuration for benchmarking tests to be reused for similar cases
// This struct can be factored out to an accessible location should additional benchmarking suites be added
type benchmarkConfig struct {
iterations int // the number of iterations to attempt for a particular entry
maxDur time.Duration // the maximum time to spend on a particular entry even if not all iterations are complete
benchmarkMatchers []types.GomegaMatcher // matchers representing the assertions we wish to make for a particular entry
}

// Tests are run as part of the "Nightly" action in a GHA using the default Linux runner
// More info on that machine can be found here: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
// When developing new tests, users should manually run that action in order to test performance under the same parameters
Expand Down Expand Up @@ -83,7 +75,7 @@ var _ = Describe("Translation - Benchmarking Tests", decorators.Performance, Lab
// We measure the duration of the translation of the snapshot, benchmarking according to the benchmarkConfig
// Labels are used to add context to the entry description
DescribeTable("Benchmark table",
func(snapBuilder *gloohelpers.ScaledSnapshotBuilder, config benchmarkConfig, labels ...string) {
func(snapBuilder *gloohelpers.ScaledSnapshotBuilder, config *gloohelpers.BenchmarkConfig, labels ...string) {
var (
apiSnap *v1snap.ApiSnapshot
proxy *v1.Proxy
Expand All @@ -95,6 +87,11 @@ var _ = Describe("Translation - Benchmarking Tests", decorators.Performance, Lab
tooFastWarningCount int
)

// Translating logs at info level, which is very noisy when running repeatedly
originalLogLevel := contextutils.GetLogLevel()
contextutils.SetLogLevel(zap.ErrorLevel)
defer contextutils.SetLogLevel(originalLogLevel)

apiSnap = snapBuilder.Build()

params := plugins.Params{
Expand All @@ -105,7 +102,7 @@ var _ = Describe("Translation - Benchmarking Tests", decorators.Performance, Lab
Expect(apiSnap.Proxies).NotTo(BeEmpty())
proxy = apiSnap.Proxies[0]

desc := generateDesc(snapBuilder, config, labels...)
desc := gloohelpers.GenerateBenchmarkDesc(snapBuilder, config, labels...)

experiment := gmeasure.NewExperiment(fmt.Sprintf("Experiment - %s", desc))

Expand Down Expand Up @@ -135,7 +132,7 @@ var _ = Describe("Translation - Benchmarking Tests", decorators.Performance, Lab
// If desired, a field can be added to benchmarkConfig to allow benchmarking according to User and/or
// System time
experiment.RecordDuration(desc, res.Total)
}, gmeasure.SamplingConfig{N: config.iterations, Duration: config.maxDur})
}, gmeasure.SamplingConfig{N: config.Iterations, Duration: config.MaxDur})

if tooFastWarningCount > 0 {
logger := contextutils.LoggerFrom(params.Ctx)
Expand All @@ -144,9 +141,9 @@ var _ = Describe("Translation - Benchmarking Tests", decorators.Performance, Lab

durations := experiment.Get(desc).Durations

Expect(durations).Should(And(config.benchmarkMatchers...))
Expect(durations).Should(And(config.GetMatchers()...))
},
generateDesc, // generate descriptions for table entries with nil descriptions
gloohelpers.GenerateBenchmarkDesc, // generate descriptions for table entries with nil descriptions
Entry("basic", gloohelpers.NewInjectedSnapshotBuilder(basicSnap), basicConfig),
Entry(nil, gloohelpers.NewScaledSnapshotBuilder().WithUpstreamCount(10).WithEndpointCount(1), basicConfig, "upstream scale"),
Entry(nil, gloohelpers.NewScaledSnapshotBuilder().WithUpstreamCount(1000).WithEndpointCount(1), oneKUpstreamsConfig, "upstream scale"),
Expand All @@ -160,20 +157,6 @@ var _ = Describe("Translation - Benchmarking Tests", decorators.Performance, Lab
)
})

func generateDesc(b *gloohelpers.ScaledSnapshotBuilder, _ benchmarkConfig, labels ...string) string {
labelPrefix := ""
if len(labels) > 0 {
labelPrefix = fmt.Sprintf("(%s) ", strings.Join(labels, ", "))
}

if b.HasInjectedSnapshot() {
return fmt.Sprintf("%sinjected snapshot", labelPrefix)
}

// If/when additional Snapshot fields are included in testing, the description should be updated accordingly
return fmt.Sprintf("%s%d endpoint(s), %d upstream(s)", labelPrefix, b.EndpointCount(), b.UpstreamCount())
}

// Test assets: Add blocks for logical groupings of tests, including:
// - in-line snapshot definitions for tests that require granularly-configured/heterogeneous resources (ie testing a particular field or feature)
// - benchmarkConfigs for particular groups of use cases depending on processing time requirements/expectations
Expand All @@ -191,23 +174,31 @@ var basicSnap = &v1snap.ApiSnapshot{
Upstreams: []*v1.Upstream{gloohelpers.Upstream(0)},
}

var basicConfig = benchmarkConfig{
iterations: 1000,
maxDur: 10 * time.Second,
benchmarkMatchers: []types.GomegaMatcher{
var basicConfig = &gloohelpers.BenchmarkConfig{
Iterations: 1000,
MaxDur: 10 * time.Second,
GhaMatchers: []types.GomegaMatcher{
matchers.HaveMedianLessThan(50 * time.Millisecond),
matchers.HavePercentileLessThan(90, 100*time.Millisecond),
},
LocalMatchers: []types.GomegaMatcher{
matchers.HaveMedianLessThan(2 * time.Millisecond),
matchers.HavePercentileLessThan(90, 4*time.Millisecond),
},
}

/* 1k Upstreams Scale Test */
var oneKUpstreamsConfig = benchmarkConfig{
iterations: 100,
maxDur: 30 * time.Second,
benchmarkMatchers: []types.GomegaMatcher{
var oneKUpstreamsConfig = &gloohelpers.BenchmarkConfig{
Iterations: 100,
MaxDur: 30 * time.Second,
GhaMatchers: []types.GomegaMatcher{
matchers.HaveMedianLessThan(time.Second),
matchers.HavePercentileLessThan(90, 2*time.Second),
},
LocalMatchers: []types.GomegaMatcher{
matchers.HaveMedianLessThan(40 * time.Millisecond),
matchers.HavePercentileLessThan(90, 80*time.Millisecond),
},
}

/* Upstream SNI Test */
Expand Down
36 changes: 35 additions & 1 deletion test/helpers/benchmark.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
package helpers

import "time"
import (
"fmt"
"os"
"strings"
"time"

"github.com/solo-io/gloo/test/testutils"

"github.com/onsi/gomega/types"
)

// Result represents the result of measuring a function's execution time.
type Result struct {
Expand All @@ -11,3 +20,28 @@ type Result struct {
// Time spent in user mode + kernel mode
Total time.Duration
}

// BenchmarkConfig allows configuration for benchmarking tests to be reused for similar cases
// This struct can be factored out to an accessible location should additional benchmarking suites be added
type BenchmarkConfig struct {
Iterations int // the number of iterations to attempt for a particular entry
MaxDur time.Duration // the maximum time to spend on a particular entry even if not all iterations are complete
LocalMatchers []types.GomegaMatcher // matchers representing the assertions we wish to make for a particular entry when running locally
inFocus7 marked this conversation as resolved.
Show resolved Hide resolved
GhaMatchers []types.GomegaMatcher // matchers representing the assertions we wish to make for a particular entry when running in a GHA
}

func (bc *BenchmarkConfig) GetMatchers() []types.GomegaMatcher {
if os.Getenv(testutils.GithubAction) != "" {
return bc.GhaMatchers
}
return bc.LocalMatchers
}

func GenerateBenchmarkDesc(b *ScaledSnapshotBuilder, _ *BenchmarkConfig, labels ...string) string {
labelPrefix := ""
if len(labels) > 0 {
labelPrefix = fmt.Sprintf("(%s) ", strings.Join(labels, ", "))
}

return fmt.Sprintf("%s%s", labelPrefix, b.description())
}
9 changes: 9 additions & 0 deletions test/helpers/scaled_snapshots.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ func (b *ScaledSnapshotBuilder) Build() *gloosnapshot.ApiSnapshot {
}
}

func (b *ScaledSnapshotBuilder) description() string {
if b.HasInjectedSnapshot() {
return "injected snapshot"
}

// If/when additional Snapshot fields are included in testing, the description should be updated accordingly
return fmt.Sprintf("%d endpoint(s), %d upstream(s)", b.EndpointCount(), b.UpstreamCount())
}

func upMeta(i int) *core.Metadata {
return &core.Metadata{
Name: fmt.Sprintf("test-%06d", i),
Expand Down
10 changes: 10 additions & 0 deletions test/helpers/upstreams.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package helpers
import (
"fmt"

"github.com/solo-io/gloo/projects/gloo/pkg/api/external/envoy/api/v2/core"
v1 "github.com/solo-io/gloo/projects/gloo/pkg/api/v1"
"github.com/solo-io/gloo/projects/gloo/pkg/api/v1/ssl"
)

// UpstreamBuilder contains options for building Upstreams to be included in scaled Snapshots
type UpstreamBuilder struct {
sniPattern sniPattern

healthChecks []*core.HealthCheck
}

type sniPattern int
Expand All @@ -34,9 +37,16 @@ func (b *UpstreamBuilder) WithConsistentSni() *UpstreamBuilder {
return b
}

func (b *UpstreamBuilder) WithHealthChecks(healthChecks []*core.HealthCheck) *UpstreamBuilder {
b.healthChecks = healthChecks
return b
}

func (b *UpstreamBuilder) Build(i int) *v1.Upstream {
up := Upstream(i)

up.HealthChecks = b.healthChecks

switch b.sniPattern {
case uniqueSni:
up.SslConfig = &ssl.UpstreamSslConfig{
Expand Down
4 changes: 4 additions & 0 deletions test/testutils/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ const (
// ServiceLogLevel is used to set the log level for the test services. See services/logging.go for more details
ServiceLogLevel = "SERVICE_LOG_LEVEL"

// GithubAction is used by Github Actions and is the name of the currently running action or ID of a step
// https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
GithubAction = "GITHUB_ACITON"

// GcloudBuildId is used by Cloudbuild to identify the build id
// This is set when running tests in Cloudbuild
GcloudBuildId = "GCLOUD_BUILD_ID"
Expand Down
Loading