Skip to content

Commit 4840f50

Browse files
authored
chore: mixed benchmark test (#3223)
* feat(benchmarking): add structured result output via BENCH_RESULT_OUTPUT emit full benchmark run metadata (config, tags, metrics, block range, spamoor stats) as JSON when BENCH_RESULT_OUTPUT is set. consumed by external matrix runner for table generation. * fix: address PR review feedback for structured benchmark output - Deduplicate overhead/reth-rate computation: move stats-based helpers to helpers.go, make span-based wrappers delegate to them - Fix sub-millisecond precision loss in engine span timings by using microsecond-based float division instead of integer truncation - Add spamoor stats to TestGasBurner for consistency with other tests * refactor: make spamoor config fully configurable via BENCH_* env vars - Add MaxPending, Rebroadcast, BaseFee, TipFee to benchConfig - Fix ERC20 test hardcoding max_wallets=200 instead of using cfg - Replace all hardcoded spamoor params with cfg fields across tests * feat: extract host metadata from OTEL resource attributes in trace spans - Add resourceAttrs struct with host, OS, and service fields - Extract attributes from VictoriaTraces LogsQL span data via resourceAttrCollector interface - Include host metadata in structured benchmark result JSON * fix: defer emitRunResult so results are written even on test failure Move emitRunResult into a deferred closure in all three test functions. If the test fails after metrics are collected, the structured JSON is still written. If it fails before result data exists, the defer is a no-op. * fix: state pressure benchmark CI failure and align with other tests Remove the 3-second sleep before requireSpammersRunning that caused all transactions to be mined before the measurement window started, leaving SteadyState at 0s. Also add deferred emitRunResult, configurable spamoor params, and spamoorStats collection to match the other benchmark tests. * fix: use deployment-level service names for trace queries in external mode In external mode the sequencer reports spans as "ev-node" (not the test-specific name like "ev-node-erc20"), so trace queries returned zero spans. Store service names on env: local mode uses the test-specific name, external mode defaults to "ev-node"/"ev-reth" with BENCH_EVNODE_SERVICE_NAME/BENCH_EVRETH_SERVICE_NAME overrides. * perf: use limit=1 for resource attribute trace queries fetchResourceAttrs only needs one span but was streaming the full result set from VictoriaTraces. Add limit=1 to the LogsQL query to avoid wasting bandwidth on long-lived instances with many spans. * docs: add missing doc comments to run_result.go functions * feat: adding mixed benchmarking test * fix: address review feedback on mixed benchmark test - fail test on waitForMetricTarget timeout instead of silently continuing - bump default NumSpammers from 2 to 4 to satisfy mixed workload minimum - replace time.Sleep with metric polling for contract deployment readiness - add panic guard to distributeSpammers for total < 4 - add table-driven unit tests for distributeSpammers
1 parent cc9f965 commit 4840f50

9 files changed

Lines changed: 339 additions & 11 deletions

File tree

apps/grpc/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ require (
8282
github.com/googleapis/gax-go/v2 v2.19.0 // indirect
8383
github.com/gorilla/websocket v1.5.3 // indirect
8484
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
85-
github.com/hashicorp/go-hclog v1.6.2 // indirect
85+
github.com/hashicorp/go-hclog v1.6.3 // indirect
8686
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
8787
github.com/hashicorp/go-metrics v0.5.4 // indirect
8888
github.com/hashicorp/go-msgpack v0.5.5 // indirect

apps/grpc/go.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,8 +650,9 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
650650
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
651651
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
652652
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
653-
github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I=
654653
github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
654+
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
655+
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
655656
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
656657
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
657658
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=

execution/evm/test/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NM
390390
github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
391391
github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
392392
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
393-
github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I=
394-
github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
393+
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
394+
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
395395
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
396396
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
397397
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ require (
102102
github.com/googleapis/gax-go/v2 v2.19.0 // indirect
103103
github.com/gorilla/websocket v1.5.3 // indirect
104104
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
105-
github.com/hashicorp/go-hclog v1.6.2 // indirect
105+
github.com/hashicorp/go-hclog v1.6.3 // indirect
106106
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
107107
github.com/hashicorp/go-metrics v0.5.4 // indirect
108108
github.com/hashicorp/go-msgpack v0.5.5 // indirect

go.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,9 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
331331
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
332332
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
333333
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
334-
github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I=
335334
github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
335+
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
336+
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
336337
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
337338
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
338339
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=

test/e2e/benchmark/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func newBenchConfig(serviceName string) benchConfig {
4141
SlotDuration: envOrDefault("BENCH_SLOT_DURATION", "250ms"),
4242
GasLimit: envOrDefault("BENCH_GAS_LIMIT", ""),
4343
ScrapeInterval: envOrDefault("BENCH_SCRAPE_INTERVAL", "1s"),
44-
NumSpammers: envInt("BENCH_NUM_SPAMMERS", 2),
44+
NumSpammers: envInt("BENCH_NUM_SPAMMERS", 4),
4545
CountPerSpammer: envInt("BENCH_COUNT_PER_SPAMMER", 5000),
4646
Throughput: envInt("BENCH_THROUGHPUT", 200),
4747
WarmupTxs: envInt("BENCH_WARMUP_TXS", 200),
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//go:build evm
2+
3+
package benchmark
4+
5+
import (
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestDistributeSpammers(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
total int
15+
pcts [4]int
16+
expected [4]int
17+
}{
18+
{
19+
name: "minimum total equals types",
20+
total: 4,
21+
pcts: [4]int{40, 30, 20, 10},
22+
expected: [4]int{1, 1, 1, 1},
23+
},
24+
{
25+
name: "equal percentages",
26+
total: 8,
27+
pcts: [4]int{25, 25, 25, 25},
28+
expected: [4]int{2, 2, 2, 2},
29+
},
30+
{
31+
name: "default mix 8 spammers",
32+
total: 8,
33+
pcts: [4]int{40, 30, 20, 10},
34+
expected: [4]int{3, 2, 2, 1},
35+
},
36+
{
37+
name: "large total distributes proportionally",
38+
total: 104,
39+
pcts: [4]int{40, 30, 20, 10},
40+
expected: [4]int{41, 31, 21, 11},
41+
},
42+
{
43+
name: "sum of counts equals total",
44+
total: 7,
45+
pcts: [4]int{40, 30, 20, 10},
46+
expected: [4]int{2, 2, 2, 1},
47+
},
48+
{
49+
name: "5 spammers with uneven split",
50+
total: 5,
51+
pcts: [4]int{40, 30, 20, 10},
52+
expected: [4]int{2, 1, 1, 1},
53+
},
54+
}
55+
56+
for _, tt := range tests {
57+
t.Run(tt.name, func(t *testing.T) {
58+
result := distributeSpammers(tt.total, tt.pcts)
59+
require.Equal(t, tt.expected, result)
60+
61+
sum := result[0] + result[1] + result[2] + result[3]
62+
require.Equal(t, tt.total, sum, "sum of distributed spammers must equal total")
63+
64+
for i, c := range result {
65+
require.GreaterOrEqual(t, c, 1, "type %d must have at least 1 spammer", i)
66+
}
67+
})
68+
}
69+
}
70+
71+
func TestDistributeSpammersPanicsOnLowTotal(t *testing.T) {
72+
require.Panics(t, func() {
73+
distributeSpammers(3, [4]int{40, 30, 20, 10})
74+
})
75+
require.Panics(t, func() {
76+
distributeSpammers(0, [4]int{25, 25, 25, 25})
77+
})
78+
}

test/e2e/benchmark/helpers.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -471,11 +471,9 @@ func waitForMetricTarget(t testing.TB, name string, poll func() (float64, error)
471471
}
472472
select {
473473
case <-ctx.Done():
474-
t.Logf("metric %s: context cancelled (target %.0f)", name, target)
475-
return
474+
t.Fatalf("metric %s: context cancelled (target %.0f)", name, target)
476475
case <-timer.C:
477-
t.Logf("metric %s did not reach target %.0f within %v", name, target, timeout)
478-
return
476+
t.Fatalf("metric %s did not reach target %.0f within %v", name, target, timeout)
479477
case <-ticker.C:
480478
}
481479
}

0 commit comments

Comments
 (0)