diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..00cd37ee --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,44 @@ +name: Benchmark + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: write + deployments: write + +jobs: + test: + name: Benchmark + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + - uses: actions/setup-node@v4 + with: + node-version-file: web/package.json + cache: "npm" + cache-dependency-path: web/package-lock.json + - run: make web-install web-build + - run: make test-env-start + - run: make install-deps + - run: make bench + - uses: benchmark-action/github-action-benchmark@v1 + with: + name: Gravity Benchmark + tool: 'go' + output-file-path: test-output + github-token: ${{ secrets.GITHUB_TOKEN }} + auto-push: true + # Show alert with commit comment on detecting possible performance regression + alert-threshold: '200%' + comment-on-alert: true + fail-on-alert: true + alert-comment-cc-users: '@BeryJu' + summary-always: true diff --git a/Makefile b/Makefile index 8741851f..22e2fac8 100644 --- a/Makefile +++ b/Makefile @@ -169,7 +169,7 @@ test: internal/resources/macoui internal/resources/blocky internal/resources/tft export ETCD_ENDPOINT="localhost:2385" export DEBUG="true" export LISTEN_ONLY="true" - go run -v ${PWD} cli etcdctl del --prefix / + export CI="true" go test \ -p 1 \ -coverprofile=coverage.txt \ @@ -179,3 +179,16 @@ test: internal/resources/macoui internal/resources/blocky internal/resources/tft $(shell go list ./... | grep -v ./api) \ 2>&1 | tee test-output go tool cover -html coverage.txt -o coverage.html + +bench: internal/resources/macoui internal/resources/blocky internal/resources/tftp + export BOOTSTRAP_ROLES="dns;dhcp;api;discovery;backup;debug;tsdb;tftp" + export ETCD_ENDPOINT="localhost:2385" + export LISTEN_ONLY="true" + export LOG_LEVEL="FATAL" + export CI="true" + go test \ + -run=^$$ \ + -bench=^Benchmark \ + -benchmem \ + $(shell go list ./... | grep -v ./api) \ + | tee test-output diff --git a/pkg/extconfig/log.go b/pkg/extconfig/log.go index 2927d8eb..7592550c 100644 --- a/pkg/extconfig/log.go +++ b/pkg/extconfig/log.go @@ -41,6 +41,9 @@ func (e *ExtConfig) BuildLoggerWithLevel(l zapcore.Level) *zap.Logger { config.EncoderConfig = zap.NewDevelopmentEncoderConfig() config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder } + if CI() { + config.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder + } config.EncoderConfig.EncodeDuration = zapcore.MillisDurationEncoder log, err := config.Build() if err != nil { diff --git a/pkg/extconfig/version.go b/pkg/extconfig/version.go index d3696b15..b9b627fa 100644 --- a/pkg/extconfig/version.go +++ b/pkg/extconfig/version.go @@ -11,8 +11,12 @@ var ( BuildHash = "" ) +func CI() bool { + return strings.EqualFold(os.Getenv("CI"), "true") +} + func FullVersion() string { - if os.Getenv("CI") == "true" { + if CI() { Version = "99.99.99" BuildHash = "test" } diff --git a/pkg/roles/dns/handler_etcd_bench_test.go b/pkg/roles/dns/handler_etcd_bench_test.go new file mode 100644 index 00000000..2a519da8 --- /dev/null +++ b/pkg/roles/dns/handler_etcd_bench_test.go @@ -0,0 +1,66 @@ +package dns_test + +import ( + "testing" + + "beryju.io/gravity/pkg/instance" + "beryju.io/gravity/pkg/roles/dns" + "beryju.io/gravity/pkg/roles/dns/types" + "beryju.io/gravity/pkg/tests" + d "github.com/miekg/dns" +) + +func BenchmarkRoleDNS_Etcd(b *testing.B) { + defer tests.Setup(nil)() + rootInst := instance.New() + ctx := tests.Context() + inst := rootInst.ForRole("dns", ctx) + tests.PanicIfError(inst.KV().Put( + ctx, + inst.KV().Key( + types.KeyRole, + types.KeyZones, + TestZone, + ).String(), + tests.MustJSON(dns.Zone{ + HandlerConfigs: []map[string]interface{}{ + { + "type": "etcd", + }, + }, + }), + )) + tests.PanicIfError(inst.KV().Put( + ctx, + inst.KV().Key( + types.KeyRole, + types.KeyZones, + TestZone, + "foo", + types.DNSRecordTypeA, + "0", + ).String(), + tests.MustJSON(dns.Record{ + Data: "10.1.2.3", + }), + )) + + role := dns.New(inst) + _ = role.Start(ctx, RoleConfig()) + defer role.Stop() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fw := NewNullDNSWriter() + role.Handler(fw, &d.Msg{ + Question: []d.Question{ + { + Name: "foo.example.com.", + Qtype: d.TypeA, + Qclass: d.ClassINET, + }, + }, + }) + _ = fw.Msg() + } +} diff --git a/pkg/roles/dns/handler_memory_bench_test.go b/pkg/roles/dns/handler_memory_bench_test.go new file mode 100644 index 00000000..10e7c35e --- /dev/null +++ b/pkg/roles/dns/handler_memory_bench_test.go @@ -0,0 +1,66 @@ +package dns_test + +import ( + "testing" + + "beryju.io/gravity/pkg/instance" + "beryju.io/gravity/pkg/roles/dns" + "beryju.io/gravity/pkg/roles/dns/types" + "beryju.io/gravity/pkg/tests" + d "github.com/miekg/dns" +) + +func BenchmarkRoleDNS_Memory(b *testing.B) { + defer tests.Setup(nil)() + rootInst := instance.New() + ctx := tests.Context() + inst := rootInst.ForRole("dns", ctx) + tests.PanicIfError(inst.KV().Put( + ctx, + inst.KV().Key( + types.KeyRole, + types.KeyZones, + ".", + ).String(), + tests.MustJSON(dns.Zone{ + HandlerConfigs: []map[string]interface{}{ + { + "type": "memory", + }, + }, + }), + )) + tests.PanicIfError(inst.KV().Put( + ctx, + inst.KV().Key( + types.KeyRole, + types.KeyZones, + ".", + "foo", + types.DNSRecordTypeA, + "0", + ).String(), + tests.MustJSON(dns.Record{ + Data: "10.1.2.3", + }), + )) + + role := dns.New(inst) + _ = role.Start(ctx, RoleConfig()) + defer role.Stop() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fw := NewNullDNSWriter() + role.Handler(fw, &d.Msg{ + Question: []d.Question{ + { + Name: "foo.", + Qtype: d.TypeA, + Qclass: d.ClassINET, + }, + }, + }) + _ = fw.Msg() + } +} diff --git a/pkg/roles/dns/role_bench_test.go b/pkg/roles/dns/role_bench_test.go new file mode 100644 index 00000000..49e82e1c --- /dev/null +++ b/pkg/roles/dns/role_bench_test.go @@ -0,0 +1,60 @@ +package dns_test + +import ( + "testing" + + "beryju.io/gravity/pkg/instance" + "beryju.io/gravity/pkg/roles/dns" + "beryju.io/gravity/pkg/roles/dns/types" + "beryju.io/gravity/pkg/tests" + d "github.com/miekg/dns" +) + +func BenchmarkRoleDNS_DefaultRootZone(b *testing.B) { + defer tests.Setup(nil)() + rootInst := instance.New() + ctx := tests.Context() + inst := rootInst.ForRole("dns", ctx) + tests.PanicIfError(inst.KV().Put( + ctx, + inst.KV().Key( + types.KeyRole, + types.KeyZones, + ".", + ).String(), + tests.MustJSON(dns.Zone{ + HandlerConfigs: []map[string]interface{}{ + { + "type": "memory", + }, + { + "type": "etcd", + }, + { + "type": "forward_ip", + "to": []string{"127.0.0.1:1053"}, + "cache_ttl": 3600, + }, + }, + }), + )) + + role := dns.New(inst) + _ = role.Start(ctx, RoleConfig()) + defer role.Stop() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + fw := NewNullDNSWriter() + role.Handler(fw, &d.Msg{ + Question: []d.Question{ + { + Name: "gravity.beryju.io.", + Qtype: d.TypeA, + Qclass: d.ClassINET, + }, + }, + }) + _ = fw.Msg() + } +} diff --git a/pkg/tests/utils.go b/pkg/tests/utils.go index 64728117..9029fb01 100644 --- a/pkg/tests/utils.go +++ b/pkg/tests/utils.go @@ -85,7 +85,9 @@ func ResetEtcd(t *testing.T) { "/", clientv3.WithPrefix(), ) - assert.NoError(t, err) + if t != nil { + assert.NoError(t, err) + } } func Setup(t *testing.T) func() {