Skip to content

Commit e2bcbaf

Browse files
Measure bloom filter AppGossip hit rate (#4085)
Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org> Co-authored-by: Stephen Buttolph <stephen@avalabs.org>
1 parent 90548b9 commit e2bcbaf

File tree

3 files changed

+49
-2
lines changed

3 files changed

+49
-2
lines changed

network/p2p/gossip/gossip.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ type Metrics struct {
101101
tracking *prometheus.GaugeVec
102102
trackingLifetimeAverage prometheus.Gauge
103103
topValidators *prometheus.GaugeVec
104+
bloomFilterHitRate prometheus.Histogram
104105
}
105106

106107
// NewMetrics returns a common set of metrics
@@ -109,6 +110,15 @@ func NewMetrics(
109110
namespace string,
110111
) (Metrics, error) {
111112
m := Metrics{
113+
bloomFilterHitRate: prometheus.NewHistogram(prometheus.HistogramOpts{
114+
Namespace: namespace,
115+
Name: "bloomfilter_hit_rate",
116+
Help: "Hit rate (%) of the bloom filter sent by pull gossip",
117+
// Buckets are (-∞, 0], (0, 25%], (25%, 50%], (50%, 75%], (75%, ∞).
118+
// 0% is placed into its own bucket so that useless bloom filters
119+
// can be inspected individually.
120+
Buckets: prometheus.LinearBuckets(0, 25, 4),
121+
}),
112122
count: prometheus.NewCounterVec(
113123
prometheus.CounterOpts{
114124
Namespace: namespace,
@@ -148,6 +158,7 @@ func NewMetrics(
148158
),
149159
}
150160
err := errors.Join(
161+
metrics.Register(m.bloomFilterHitRate),
151162
metrics.Register(m.count),
152163
metrics.Register(m.bytes),
153164
metrics.Register(m.tracking),

network/p2p/gossip/gossip_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func TestGossiperGossip(t *testing.T) {
5757
responder []*testTx // what the peer we're requesting gossip from has
5858
expectedPossibleValues []*testTx // possible values we can have
5959
expectedLen int
60+
expectedHitRate float64
6061
}{
6162
{
6263
name: "no gossip - no one knows anything",
@@ -75,13 +76,15 @@ func TestGossiperGossip(t *testing.T) {
7576
responder: []*testTx{{id: ids.ID{0}}},
7677
expectedPossibleValues: []*testTx{{id: ids.ID{0}}},
7778
expectedLen: 1,
79+
expectedHitRate: 100,
7880
},
7981
{
8082
name: "gossip - requester knows nothing",
8183
targetResponseSize: 1024,
8284
responder: []*testTx{{id: ids.ID{0}}},
8385
expectedPossibleValues: []*testTx{{id: ids.ID{0}}},
8486
expectedLen: 1,
87+
expectedHitRate: 0,
8588
},
8689
{
8790
name: "gossip - requester knows less than responder",
@@ -90,13 +93,15 @@ func TestGossiperGossip(t *testing.T) {
9093
responder: []*testTx{{id: ids.ID{0}}, {id: ids.ID{1}}},
9194
expectedPossibleValues: []*testTx{{id: ids.ID{0}}, {id: ids.ID{1}}},
9295
expectedLen: 2,
96+
expectedHitRate: 50,
9397
},
9498
{
9599
name: "gossip - target response size exceeded",
96100
targetResponseSize: 32,
97101
responder: []*testTx{{id: ids.ID{0}}, {id: ids.ID{1}}, {id: ids.ID{2}}},
98102
expectedPossibleValues: []*testTx{{id: ids.ID{0}}, {id: ids.ID{1}}, {id: ids.ID{2}}},
99103
expectedLen: 2,
104+
expectedHitRate: 0,
100105
},
101106
}
102107

@@ -123,6 +128,12 @@ func TestGossiperGossip(t *testing.T) {
123128

124129
metrics, err := NewMetrics(prometheus.NewRegistry(), "")
125130
require.NoError(err)
131+
132+
testHistogram := &testHistogram{
133+
Histogram: metrics.bloomFilterHitRate,
134+
}
135+
metrics.bloomFilterHitRate = testHistogram
136+
126137
marshaller := testMarshaller{}
127138
handler := NewHandler[*testTx](
128139
logging.NoLog{},
@@ -175,6 +186,8 @@ func TestGossiperGossip(t *testing.T) {
175186

176187
require.Len(requestSet.txs, tt.expectedLen)
177188
require.Subset(tt.expectedPossibleValues, maps.Values(requestSet.txs))
189+
require.Equal(len(tt.responder) > 0, testHistogram.observed)
190+
require.InDelta(tt.expectedHitRate, testHistogram.observedVal, 0)
178191

179192
// we should not receive anything that we already had before we
180193
// requested the gossip
@@ -611,3 +624,15 @@ type testValidatorSet struct {
611624
func (t testValidatorSet) Has(_ context.Context, nodeID ids.NodeID) bool {
612625
return t.validators.Contains(nodeID)
613626
}
627+
628+
type testHistogram struct {
629+
prometheus.Histogram
630+
observedVal float64
631+
observed bool
632+
}
633+
634+
func (t *testHistogram) Observe(value float64) {
635+
t.Histogram.Observe(value)
636+
t.observedVal = value
637+
t.observed = true
638+
}

network/p2p/gossip/handler.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,19 @@ func (h Handler[T]) AppRequest(_ context.Context, _ ids.NodeID, _ time.Time, req
5050
return nil, p2p.ErrUnexpected
5151
}
5252

53-
responseSize := 0
54-
gossipBytes := make([][]byte, 0)
53+
var (
54+
hits float64
55+
total float64
56+
responseSize int
57+
gossipBytes [][]byte
58+
)
5559
h.set.Iterate(func(gossipable T) bool {
60+
total++
5661
gossipID := gossipable.GossipID()
5762

5863
// filter out what the requesting peer already knows about
5964
if bloom.Contains(filter, gossipID[:], salt[:]) {
65+
hits++
6066
return true
6167
}
6268

@@ -77,6 +83,11 @@ func (h Handler[T]) AppRequest(_ context.Context, _ ids.NodeID, _ time.Time, req
7783
return nil, p2p.ErrUnexpected
7884
}
7985

86+
if total > 0 {
87+
hitRate := float64(hits) / float64(total)
88+
h.metrics.bloomFilterHitRate.Observe(100 * hitRate)
89+
}
90+
8091
if err := h.metrics.observeMessage(sentPullLabels, len(gossipBytes), responseSize); err != nil {
8192
return nil, p2p.ErrUnexpected
8293
}

0 commit comments

Comments
 (0)