From 34a2de0d5474a0d413662c47378561a83cd8cbb1 Mon Sep 17 00:00:00 2001 From: Aditya Thebe Date: Fri, 6 Dec 2024 12:15:43 +0545 Subject: [PATCH] feat: benchmarks --- Makefile | 4 + cmd/bench/bench.go | 104 ++++++++++++++++++++++ shutdown/shutdown.go | 7 +- tests/config_generator_test.go | 13 +-- tests/config_relationship_test.go | 13 +-- tests/config_type_summary_test.go | 15 ++-- tests/{ => generator}/config_generator.go | 2 +- tests/setup/common.go | 86 +++++++++--------- 8 files changed, 183 insertions(+), 61 deletions(-) create mode 100644 cmd/bench/bench.go rename tests/{ => generator}/config_generator.go (99%) diff --git a/Makefile b/Makefile index ef29a332..0120c720 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,10 @@ ginkgo: test: ginkgo ginkgo -r -v +.PHONY: bench +bench: + go run cmd/bench/bench.go + fmt: go fmt ./... diff --git a/cmd/bench/bench.go b/cmd/bench/bench.go new file mode 100644 index 00000000..0c2e8add --- /dev/null +++ b/cmd/bench/bench.go @@ -0,0 +1,104 @@ +package main + +import ( + "flag" + "time" + + "github.com/flanksource/commons/logger" + "github.com/flanksource/duty/context" + "github.com/flanksource/duty/shutdown" + pkgGenerator "github.com/flanksource/duty/tests/generator" + "github.com/flanksource/duty/tests/setup" +) + +var count = 2_000 + +func generateConfigItems(ctx context.Context, count int) error { + start := time.Now() + for { + generator := pkgGenerator.ConfigGenerator{ + Nodes: pkgGenerator.ConfigTypeRequirements{ + Count: 3, + }, + Namespaces: pkgGenerator.ConfigTypeRequirements{ + Count: 10, + }, + DeploymentPerNamespace: pkgGenerator.ConfigTypeRequirements{ + Count: 10, + }, + ReplicaSetPerDeployment: pkgGenerator.ConfigTypeRequirements{ + Count: 2, + Deleted: 1, + }, + PodsPerReplicaSet: pkgGenerator.ConfigTypeRequirements{ + Count: 2, + NumChangesPerConfig: 1, + NumInsightsPerConfig: 2, + }, + Tags: map[string]string{ + "cluster": "homelab", + }, + } + + generator.GenerateKubernetes() + if err := generator.Save(ctx.DB()); err != nil { + return err + } + + var totalConfigs int64 + if err := ctx.DB().Table("config_items").Count(&totalConfigs).Error; err != nil { + return err + } + + if totalConfigs > int64(count) { + break + } + + logger.Infof("created configs: %d/%d", totalConfigs, count) + } + + var configs int64 + if err := ctx.DB().Table("config_items").Count(&configs).Error; err != nil { + return err + } + + var changes int64 + if err := ctx.DB().Table("config_changes").Count(&changes).Error; err != nil { + return err + } + + logger.Infof("configs %d, changes: %d in %s", configs, changes, time.Since(start)) + return nil +} + +func main() { + shutdown.WaitForSignal() + flag.IntVar(&count, "count", count, "generates at least these number of configs") + flag.Parse() + + // start a postgres db with RLS disabled + if err := run(); err != nil { + shutdown.ShutdownAndExit(1, err.Error()) + } + + // TODO: run benchmark on another database RLS enabled + // can't use the same database to avoid caches from the previous benchmark. + + shutdown.ShutdownAndExit(0, "exiting ...") +} + +func run() error { + // setup a db with RLS disabled + ctx, err := setup.SetupDB("test", setup.WithoutRLS) + if err != nil { + return err + } + + if err := generateConfigItems(ctx, count); err != nil { + return err + } + + // Run fetch queries + + return nil +} diff --git a/shutdown/shutdown.go b/shutdown/shutdown.go index 872802a7..ad91b51d 100644 --- a/shutdown/shutdown.go +++ b/shutdown/shutdown.go @@ -48,7 +48,12 @@ var Shutdown = sync.OnceFunc(func() { func ShutdownAndExit(code int, msg string) { Shutdown() - logger.StandardLogger().WithSkipReportLevel(1).Errorf(msg) + if code == 0 { + logger.StandardLogger().WithSkipReportLevel(1).Infof(msg) + } else { + logger.StandardLogger().WithSkipReportLevel(1).Errorf(msg) + } + os.Exit(code) } diff --git a/tests/config_generator_test.go b/tests/config_generator_test.go index f57fb5fb..6c86e57f 100644 --- a/tests/config_generator_test.go +++ b/tests/config_generator_test.go @@ -2,26 +2,27 @@ package tests import ( "github.com/flanksource/duty/models" + pkgGenerator "github.com/flanksource/duty/tests/generator" "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = ginkgo.Describe("Config Generator", ginkgo.Ordered, func() { - generator := ConfigGenerator{ - Nodes: ConfigTypeRequirements{ + generator := pkgGenerator.ConfigGenerator{ + Nodes: pkgGenerator.ConfigTypeRequirements{ Count: 3, }, - Namespaces: ConfigTypeRequirements{ + Namespaces: pkgGenerator.ConfigTypeRequirements{ Count: 2, }, - DeploymentPerNamespace: ConfigTypeRequirements{ + DeploymentPerNamespace: pkgGenerator.ConfigTypeRequirements{ Count: 1, }, - ReplicaSetPerDeployment: ConfigTypeRequirements{ + ReplicaSetPerDeployment: pkgGenerator.ConfigTypeRequirements{ Count: 4, Deleted: 3, }, - PodsPerReplicaSet: ConfigTypeRequirements{ + PodsPerReplicaSet: pkgGenerator.ConfigTypeRequirements{ Count: 1, NumChangesPerConfig: 5, NumInsightsPerConfig: 2, diff --git a/tests/config_relationship_test.go b/tests/config_relationship_test.go index 43fcf336..62262f66 100644 --- a/tests/config_relationship_test.go +++ b/tests/config_relationship_test.go @@ -7,6 +7,7 @@ import ( "github.com/flanksource/duty/models" "github.com/flanksource/duty/query" "github.com/flanksource/duty/tests/fixtures/dummy" + pkgGenerator "github.com/flanksource/duty/tests/generator" "github.com/flanksource/duty/types" "github.com/flanksource/duty/upstream" "github.com/google/uuid" @@ -464,12 +465,12 @@ var _ = ginkgo.Describe("config relationship deletion test", func() { }) var _ = ginkgo.Describe("config relationship depth", ginkgo.Ordered, func() { - generator := ConfigGenerator{ - Nodes: ConfigTypeRequirements{Count: 3}, - Namespaces: ConfigTypeRequirements{Count: 2}, - DeploymentPerNamespace: ConfigTypeRequirements{Count: 2}, - ReplicaSetPerDeployment: ConfigTypeRequirements{Count: 4, Deleted: 3}, - PodsPerReplicaSet: ConfigTypeRequirements{Count: 1, NumChangesPerConfig: 5, NumInsightsPerConfig: 2}, + generator := pkgGenerator.ConfigGenerator{ + Nodes: pkgGenerator.ConfigTypeRequirements{Count: 3}, + Namespaces: pkgGenerator.ConfigTypeRequirements{Count: 2}, + DeploymentPerNamespace: pkgGenerator.ConfigTypeRequirements{Count: 2}, + ReplicaSetPerDeployment: pkgGenerator.ConfigTypeRequirements{Count: 4, Deleted: 3}, + PodsPerReplicaSet: pkgGenerator.ConfigTypeRequirements{Count: 1, NumChangesPerConfig: 5, NumInsightsPerConfig: 2}, Tags: map[string]string{ "test": "true", }, diff --git a/tests/config_type_summary_test.go b/tests/config_type_summary_test.go index cc294533..23bf9ff3 100644 --- a/tests/config_type_summary_test.go +++ b/tests/config_type_summary_test.go @@ -8,6 +8,7 @@ import ( "github.com/flanksource/duty/job" "github.com/flanksource/duty/models" "github.com/flanksource/duty/query" + "github.com/flanksource/duty/tests/generator" "github.com/flanksource/duty/types" ginkgo "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -96,13 +97,13 @@ var _ = ginkgo.Describe("Check config_class_summary view", ginkgo.Ordered, func( }) ginkgo.It("Should query config summary by type", func() { - gen := ConfigGenerator{} - gen.GenerateConfigItem("Test::type-A", "healthy", nil, nil, ConfigTypeRequirements{NumChangesPerConfig: 4, NumInsightsPerConfig: 3}) - gen.GenerateConfigItem("Test::type-A", "healthy", nil, nil, ConfigTypeRequirements{NumChangesPerConfig: 1, NumInsightsPerConfig: 2}) - gen.GenerateConfigItem("Test::type-A", "unhealthy", nil, nil, ConfigTypeRequirements{NumChangesPerConfig: 1, NumInsightsPerConfig: 2}) - gen.GenerateConfigItem("Test::type-B", "healthy", nil, nil, ConfigTypeRequirements{NumChangesPerConfig: 5, NumInsightsPerConfig: 1}) - gen.GenerateConfigItem("Test::type-B", "healthy", nil, nil, ConfigTypeRequirements{NumChangesPerConfig: 1, NumInsightsPerConfig: 3}) - gen.GenerateConfigItem("Test::type-C", "unhealthy", nil, nil, ConfigTypeRequirements{NumChangesPerConfig: 0, NumInsightsPerConfig: 0}) + gen := generator.ConfigGenerator{} + gen.GenerateConfigItem("Test::type-A", "healthy", nil, nil, generator.ConfigTypeRequirements{NumChangesPerConfig: 4, NumInsightsPerConfig: 3}) + gen.GenerateConfigItem("Test::type-A", "healthy", nil, nil, generator.ConfigTypeRequirements{NumChangesPerConfig: 1, NumInsightsPerConfig: 2}) + gen.GenerateConfigItem("Test::type-A", "unhealthy", nil, nil, generator.ConfigTypeRequirements{NumChangesPerConfig: 1, NumInsightsPerConfig: 2}) + gen.GenerateConfigItem("Test::type-B", "healthy", nil, nil, generator.ConfigTypeRequirements{NumChangesPerConfig: 5, NumInsightsPerConfig: 1}) + gen.GenerateConfigItem("Test::type-B", "healthy", nil, nil, generator.ConfigTypeRequirements{NumChangesPerConfig: 1, NumInsightsPerConfig: 3}) + gen.GenerateConfigItem("Test::type-C", "unhealthy", nil, nil, generator.ConfigTypeRequirements{NumChangesPerConfig: 0, NumInsightsPerConfig: 0}) for _, item := range gen.Generated.Configs { DefaultContext.DB().Create(&item) } diff --git a/tests/config_generator.go b/tests/generator/config_generator.go similarity index 99% rename from tests/config_generator.go rename to tests/generator/config_generator.go index 05e72e8f..835bc30c 100644 --- a/tests/config_generator.go +++ b/tests/generator/config_generator.go @@ -1,4 +1,4 @@ -package tests +package generator import ( "fmt" diff --git a/tests/setup/common.go b/tests/setup/common.go index 9c756adf..ef47fb71 100644 --- a/tests/setup/common.go +++ b/tests/setup/common.go @@ -1,7 +1,6 @@ package setup import ( - gocontext "context" "database/sql" "fmt" "net/http" @@ -18,6 +17,7 @@ import ( "github.com/flanksource/duty/context" "github.com/flanksource/duty/job" "github.com/flanksource/duty/models" + "github.com/flanksource/duty/shutdown" "github.com/flanksource/duty/telemetry" "github.com/flanksource/duty/tests/fixtures/dummy" "github.com/labstack/echo/v4" @@ -42,7 +42,6 @@ var dummyData dummy.DummyData var PgUrl string var postgresDBUrl string -var dbName = "test" func init() { logger.UseSlog() @@ -80,14 +79,12 @@ func MustDB() *sql.DB { return db } +var WithoutRLS = "rsl_disabled" var WithoutDummyData = "without_dummy_data" var WithExistingDatabase = "with_existing_database" var recreateDatabase = os.Getenv("DUTY_DB_CREATE") != "false" -var ShutdownHooks []func(ctx gocontext.Context) error - func findFileInPath(filename string, depth int) string { - if !path.IsAbs(filename) { cwd, _ := os.Getwd() filename = path.Join(cwd, filename) @@ -107,15 +104,25 @@ func findFileInPath(filename string, depth int) string { } func BeforeSuiteFn(args ...interface{}) context.Context { + ctx, err := SetupDB("test", args...) + if err != nil { + shutdown.ShutdownAndExit(1, fmt.Sprintf("failed to setup db: %v", err)) + } + + DefaultContext = ctx + return ctx +} + +func SetupDB(dbName string, args ...interface{}) (context.Context, error) { if err := properties.LoadFile(findFileInPath("test.properties", 2)); err != nil { logger.Errorf("Failed to load test properties: %v", err) } - ShutdownHooks = append(ShutdownHooks, telemetry.InitTracer()) + shutdown.AddHookWithPriority("telemetry", 0, func() { telemetry.InitTracer() }) - var err error importDummyData := true - + disableRLS := false + dbOptions := []duty.StartOption{duty.DisablePostgrest, duty.RunMigrations} for _, arg := range args { if arg == WithoutDummyData { importDummyData = false @@ -123,17 +130,20 @@ func BeforeSuiteFn(args ...interface{}) context.Context { if arg == WithExistingDatabase { recreateDatabase = false } + if arg == WithoutRLS { + disableRLS = true + } } - if postgresServer != nil { - return DefaultContext + if !disableRLS { + dbOptions = append(dbOptions, duty.EnableRLS) } var port int if val, ok := os.LookupEnv("TEST_DB_PORT"); ok { parsed, err := strconv.ParseInt(val, 10, 32) if err != nil { - panic(err) + return context.Context{}, err } port = int(parsed) @@ -151,52 +161,55 @@ func BeforeSuiteFn(args ...interface{}) context.Context { PgUrl = strings.Replace(url, "/postgres", "/"+dbName, 1) _ = execPostgres(postgresDBUrl, "DROP DATABASE "+dbName) if err := execPostgres(postgresDBUrl, "CREATE DATABASE "+dbName); err != nil { - panic(fmt.Sprintf("Cannot create %s: %v", dbName, err)) + return context.Context{}, fmt.Errorf("cannot create %s: %v", dbName, err) } - ShutdownHooks = append(ShutdownHooks, func(ctx gocontext.Context) error { - return execPostgres(postgresDBUrl, fmt.Sprintf("DROP DATABASE %s (FORCE)", dbName)) + shutdown.AddHookWithPriority("remote postgres", shutdown.PriorityCritical, func() { + if err := execPostgres(postgresDBUrl, fmt.Sprintf("DROP DATABASE %s (FORCE)", dbName)); err != nil { + logger.Errorf("execPostgres: %v", err) + } }) - } else if url == "" { + } else if url == "" && postgresServer == nil { config, _ := GetEmbeddedPGConfig(dbName, port) postgresServer = embeddedPG.NewDatabase(config) logger.Infof("starting embedded postgres on port %d", port) - if err = postgresServer.Start(); err != nil { - panic(err.Error()) + if err := postgresServer.Start(); err != nil { + return context.Context{}, err } logger.Infof("Started postgres on port %d", port) - ShutdownHooks = append(ShutdownHooks, func(ctx gocontext.Context) error { - logger.Infof("Stopping postgres") - return postgresServer.Stop() + shutdown.AddHookWithPriority("embedded pg", shutdown.PriorityCritical, func() { + if err := postgresServer.Stop(); err != nil { + logger.Errorf("postgresServer.Stop: %v", err) + } }) } - ctx, _, err := duty.Start("test", duty.DisablePostgrest, duty.EnableRLS, duty.RunMigrations, duty.WithUrl(PgUrl)) + dbOptions = append(dbOptions, duty.WithUrl(PgUrl)) + ctx, _, err := duty.Start(dbName, dbOptions...) if err != nil { - panic(err.Error()) + return context.Context{}, err } - DefaultContext = ctx - if err := DefaultContext.DB().Exec("SET TIME ZONE 'UTC'").Error; err != nil { - panic(err.Error()) + if err := ctx.DB().Exec("SET TIME ZONE 'UTC'").Error; err != nil { + return context.Context{}, err } - DefaultContext = DefaultContext.WithValue("db_name", dbName).WithValue("db_url", PgUrl) + ctx = ctx.WithValue("db_name", dbName).WithValue("db_url", PgUrl) if importDummyData { - dummyData = dummy.GetStaticDummyData(DefaultContext.DB()) - if err := dummyData.Delete(DefaultContext.DB()); err != nil { + dummyData = dummy.GetStaticDummyData(ctx.DB()) + if err := dummyData.Delete(ctx.DB()); err != nil { logger.Errorf(err.Error()) } - err = dummyData.Populate(DefaultContext.DB()) + err = dummyData.Populate(ctx.DB()) if err != nil { - panic(err.Error()) + return context.Context{}, err } logger.Infof("Created dummy data %v", len(dummyData.Checks)) } - DefaultContext := DefaultContext.WithKubernetes(fake.NewSimpleClientset(&v1.ConfigMap{ + ctx = ctx.WithKubernetes(fake.NewSimpleClientset(&v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "test-cm", Namespace: "default", @@ -213,18 +226,11 @@ func BeforeSuiteFn(args ...interface{}) context.Context { "foo": []byte("secret"), }}), nil) - return DefaultContext + return ctx, nil } func AfterSuiteFn() { - for _, fn := range ShutdownHooks { - if err := fn(DefaultContext); err != nil { - logger.Errorf(err.Error()) - } - } - // clear out hooks so they don't run again - ShutdownHooks = []func(ctx gocontext.Context) error{} - + shutdown.ShutdownAndExit(0, "") } // NewDB creates a new database from an existing context, and