From d183ad729fdba8ff455aa57c4094e331e75f1749 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Mon, 25 Nov 2024 01:26:11 +0100 Subject: [PATCH 1/7] dont use log colours in CI --- pkg/extconfig/log.go | 4 +++- pkg/extconfig/version.go | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/extconfig/log.go b/pkg/extconfig/log.go index 2927d8eb..3feb8356 100644 --- a/pkg/extconfig/log.go +++ b/pkg/extconfig/log.go @@ -39,7 +39,9 @@ func (e *ExtConfig) BuildLoggerWithLevel(l zapcore.Level) *zap.Logger { config.Development = false config.Encoding = "console" config.EncoderConfig = zap.NewDevelopmentEncoderConfig() - config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + if !CI() { + config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + } } config.EncoderConfig.EncodeDuration = zapcore.MillisDurationEncoder log, err := config.Build() 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" } From 97ba56a1027ec099b0f1821568bbdf37069e58f5 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Mon, 25 Nov 2024 19:33:04 +0100 Subject: [PATCH 2/7] dns: start adding benchmarks --- .github/workflows/benchmark.yml | 43 ++++++++++++++++++++++ Makefile | 15 +++++++- pkg/roles/dns/handler_memory_test.go | 55 ++++++++++++++++++++++++++++ pkg/tests/utils.go | 4 +- 4 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/benchmark.yml diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..bf7515da --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,43 @@ +name: Benchmark + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: write + deployments: write + +jobs: + test: + name: Test + 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 gen-client-ts web-build + - run: make test-env-start + - run: make install-deps gen-clean gen-client-go + - run: make bench + - name: Store benchmark result + 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 diff --git a/Makefile b/Makefile index 8741851f..4507cb7c 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 DEBUG="true" + export LISTEN_ONLY="true" + export CI="true" + go test \ + -benchmem \ + -run=^$$ \ + -bench=^Benchmark \ + $(shell go list ./... | grep -v ./api) \ + 2>&1 | tee test-output diff --git a/pkg/roles/dns/handler_memory_test.go b/pkg/roles/dns/handler_memory_test.go index 511c8ddd..a0f8ddcf 100644 --- a/pkg/roles/dns/handler_memory_test.go +++ b/pkg/roles/dns/handler_memory_test.go @@ -66,6 +66,61 @@ func TestRoleDNS_Memory(t *testing.T) { assert.Equal(t, net.ParseIP("10.1.2.3").String(), ans.(*d.A).A.String()) } +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() + } +} + func TestRoleDNS_Memory_Wildcard(t *testing.T) { defer tests.Setup(t)() rootInst := instance.New() 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() { From b36f95f61d1a1fe1bc3e8358e1967b3dc10c2a03 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Mon, 25 Nov 2024 19:57:06 +0100 Subject: [PATCH 3/7] fix output? --- .github/workflows/benchmark.yml | 2 +- Makefile | 2 +- pkg/extconfig/log.go | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index bf7515da..108f2ead 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -12,7 +12,7 @@ permissions: jobs: test: - name: Test + name: Benchmark runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/Makefile b/Makefile index 4507cb7c..c8181326 100644 --- a/Makefile +++ b/Makefile @@ -191,4 +191,4 @@ bench: internal/resources/macoui internal/resources/blocky internal/resources/tf -run=^$$ \ -bench=^Benchmark \ $(shell go list ./... | grep -v ./api) \ - 2>&1 | tee test-output + | tee test-output diff --git a/pkg/extconfig/log.go b/pkg/extconfig/log.go index 3feb8356..65041c50 100644 --- a/pkg/extconfig/log.go +++ b/pkg/extconfig/log.go @@ -39,9 +39,11 @@ func (e *ExtConfig) BuildLoggerWithLevel(l zapcore.Level) *zap.Logger { config.Development = false config.Encoding = "console" config.EncoderConfig = zap.NewDevelopmentEncoderConfig() - if !CI() { - config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder - } + config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + } + if CI() { + config.OutputPaths = []string{"stderr"} + config.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder } config.EncoderConfig.EncodeDuration = zapcore.MillisDurationEncoder log, err := config.Build() From 7be3d91fb4516e0baf699205daea4d78d446644d Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 26 Nov 2024 00:50:35 +0100 Subject: [PATCH 4/7] fix benchmark output --- Makefile | 4 ++-- pkg/extconfig/log.go | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index c8181326..22e2fac8 100644 --- a/Makefile +++ b/Makefile @@ -183,12 +183,12 @@ test: internal/resources/macoui internal/resources/blocky internal/resources/tft 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 DEBUG="true" export LISTEN_ONLY="true" + export LOG_LEVEL="FATAL" export CI="true" go test \ - -benchmem \ -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 65041c50..7592550c 100644 --- a/pkg/extconfig/log.go +++ b/pkg/extconfig/log.go @@ -42,7 +42,6 @@ func (e *ExtConfig) BuildLoggerWithLevel(l zapcore.Level) *zap.Logger { config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder } if CI() { - config.OutputPaths = []string{"stderr"} config.EncoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder } config.EncoderConfig.EncodeDuration = zapcore.MillisDurationEncoder From f97b32e08812bdd7b71d4a19594718aff8e458f5 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 26 Nov 2024 00:56:25 +0100 Subject: [PATCH 5/7] cleanup --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 108f2ead..86051434 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -25,9 +25,9 @@ jobs: node-version-file: web/package.json cache: "npm" cache-dependency-path: web/package-lock.json - - run: make web-install gen-client-ts web-build + - run: make web-install web-build - run: make test-env-start - - run: make install-deps gen-clean gen-client-go + - run: make install-deps - run: make bench - name: Store benchmark result uses: benchmark-action/github-action-benchmark@v1 From e582d43cbb87185e17bd70a5012c99ac45e45c31 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 26 Nov 2024 01:10:12 +0100 Subject: [PATCH 6/7] more benchmarks --- .github/workflows/benchmark.yml | 5 +-- pkg/roles/dns/handler_etcd_test.go | 55 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 86051434..00cd37ee 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -29,8 +29,7 @@ jobs: - run: make test-env-start - run: make install-deps - run: make bench - - name: Store benchmark result - uses: benchmark-action/github-action-benchmark@v1 + - uses: benchmark-action/github-action-benchmark@v1 with: name: Gravity Benchmark tool: 'go' @@ -41,3 +40,5 @@ jobs: alert-threshold: '200%' comment-on-alert: true fail-on-alert: true + alert-comment-cc-users: '@BeryJu' + summary-always: true diff --git a/pkg/roles/dns/handler_etcd_test.go b/pkg/roles/dns/handler_etcd_test.go index 3e28936b..e18e066c 100644 --- a/pkg/roles/dns/handler_etcd_test.go +++ b/pkg/roles/dns/handler_etcd_test.go @@ -69,6 +69,61 @@ func TestRoleDNS_Etcd(t *testing.T) { assert.Equal(t, net.ParseIP("10.1.2.3").String(), ans.(*d.A).A.String()) } +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() + } +} + // Test DNS Entry at root of zone func TestRoleDNS_Etcd_Root(t *testing.T) { defer tests.Setup(t)() From 3fef6e8a0d5c74a1ed5baf8aca72b470db6b1457 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 26 Nov 2024 01:43:13 +0100 Subject: [PATCH 7/7] separate benchmarks --- pkg/roles/dns/handler_etcd_bench_test.go | 66 ++++++++++++++++++++++ pkg/roles/dns/handler_etcd_test.go | 55 ------------------ pkg/roles/dns/handler_memory_bench_test.go | 66 ++++++++++++++++++++++ pkg/roles/dns/handler_memory_test.go | 55 ------------------ pkg/roles/dns/role_bench_test.go | 60 ++++++++++++++++++++ 5 files changed, 192 insertions(+), 110 deletions(-) create mode 100644 pkg/roles/dns/handler_etcd_bench_test.go create mode 100644 pkg/roles/dns/handler_memory_bench_test.go create mode 100644 pkg/roles/dns/role_bench_test.go 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_etcd_test.go b/pkg/roles/dns/handler_etcd_test.go index e18e066c..3e28936b 100644 --- a/pkg/roles/dns/handler_etcd_test.go +++ b/pkg/roles/dns/handler_etcd_test.go @@ -69,61 +69,6 @@ func TestRoleDNS_Etcd(t *testing.T) { assert.Equal(t, net.ParseIP("10.1.2.3").String(), ans.(*d.A).A.String()) } -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() - } -} - // Test DNS Entry at root of zone func TestRoleDNS_Etcd_Root(t *testing.T) { defer tests.Setup(t)() 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/handler_memory_test.go b/pkg/roles/dns/handler_memory_test.go index a0f8ddcf..511c8ddd 100644 --- a/pkg/roles/dns/handler_memory_test.go +++ b/pkg/roles/dns/handler_memory_test.go @@ -66,61 +66,6 @@ func TestRoleDNS_Memory(t *testing.T) { assert.Equal(t, net.ParseIP("10.1.2.3").String(), ans.(*d.A).A.String()) } -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() - } -} - func TestRoleDNS_Memory_Wildcard(t *testing.T) { defer tests.Setup(t)() rootInst := instance.New() 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() + } +}