From b520048aa6df9edf874580961968547dfd9da08e Mon Sep 17 00:00:00 2001 From: openclaw-bot Date: Fri, 15 May 2026 14:25:11 +0000 Subject: [PATCH] bench(#1199): baseline BenchmarkBuildAggregateHopContextPubkeys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Item 3/6. Aggregate path was uncovered; only the per-tx builder had a bench. Fixture: 50-node pool, 5k txs with 6-hop paths and varied sender/observer pubkeys. No assertion threshold (yet) — purpose is a baseline so future regressions surface. Local arm64 baseline: ~72ms/op, 130k allocs at 5k txs. --- cmd/server/hop_context_bench_test.go | 78 ++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/cmd/server/hop_context_bench_test.go b/cmd/server/hop_context_bench_test.go index ae847b75..bca1b04e 100644 --- a/cmd/server/hop_context_bench_test.go +++ b/cmd/server/hop_context_bench_test.go @@ -43,3 +43,81 @@ func BenchmarkBuildHopContextPubkeys(b *testing.B) { _ = buildHopContextPubkeys(tx, pm) } } + +// BenchmarkBuildAggregateHopContextPubkeys exercises the aggregate context +// builder at the hot scale called out by #1197 (subpath/topology bulk +// aggregations): ~5k txs sharing a node pool of ~50 prefixes. The aggregate +// builder unions per-tx contexts with its own dedupe map; this benchmark +// gives us a baseline so a future regression (e.g. accidental O(n²) dedupe) +// shows up immediately. No assertion threshold yet — see #1199 item 3. +func BenchmarkBuildAggregateHopContextPubkeys(b *testing.B) { + const numNodes = 50 + const numTxs = 5000 + + nodes := make([]nodeInfo, 0, numNodes) + for i := 0; i < numNodes; i++ { + nodes = append(nodes, nodeInfo{ + PublicKey: fmt.Sprintf("%012x", i*0x101010101), + Role: "repeater", + Name: fmt.Sprintf("N%d", i), + ObservationCount: i * 3, + Lat: 37.0 + float64(i)*0.01, + Lon: -122.0 - float64(i)*0.01, + HasGPS: true, + }) + } + pm := buildPrefixMap(nodes) + + txs := make([]*StoreTx, 0, numTxs) + for i := 0; i < numTxs; i++ { + hops := []string{ + nodes[(i+1)%numNodes].PublicKey[:6], + nodes[(i+3)%numNodes].PublicKey[:6], + nodes[(i+5)%numNodes].PublicKey[:6], + nodes[(i+7)%numNodes].PublicKey[:6], + nodes[(i+9)%numNodes].PublicKey[:6], + nodes[(i+11)%numNodes].PublicKey[:6], + } + pathJSON, _ := json.Marshal(hops) + decoded, _ := json.Marshal(map[string]interface{}{ + "pubKey": fmt.Sprintf("cc%010x", i), + }) + txs = append(txs, &StoreTx{ + PathJSON: string(pathJSON), + DecodedJSON: string(decoded), + ObserverID: fmt.Sprintf("dd%010x", i%32), + }) + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = buildAggregateHopContextPubkeys(txs, pm) + } +} + +// TestBuildAggregateHopContextPubkeysSmoke is a tiny correctness anchor for +// the aggregate helper: union over per-tx contexts, deduped. Lives next to +// the benchmark so the file ships an assertion (preflight gate). See #1199 +// item 3. +func TestBuildAggregateHopContextPubkeysSmoke(t *testing.T) { + pm := buildPrefixMap([]nodeInfo{{PublicKey: "aabbccddeeff"}}) + d1, _ := json.Marshal(map[string]interface{}{"pubKey": "1111111111"}) + d2, _ := json.Marshal(map[string]interface{}{"pubKey": "2222222222"}) + d3, _ := json.Marshal(map[string]interface{}{"pubKey": "1111111111"}) // dup + txs := []*StoreTx{ + {DecodedJSON: string(d1)}, + {DecodedJSON: string(d2)}, + {DecodedJSON: string(d3)}, + } + got := buildAggregateHopContextPubkeys(txs, pm) + if len(got) != 2 { + t.Fatalf("expected 2 deduped pubkeys, got %d (%v)", len(got), got) + } + if buildAggregateHopContextPubkeys(nil, pm) != nil { + t.Fatalf("nil tx slice must yield nil") + } + if buildAggregateHopContextPubkeys(txs, nil) != nil { + t.Fatalf("nil prefix map must yield nil") + } +}