eth/peerstats: bump MinLatencySamples from 10 to 100

Require substantially more samples before a peer's request-latency EMA
becomes eligible for protection. A 10-sample floor was too low: a peer
hitting 10 fast replies in a short burst could earn protection before
the slow alpha=0.01 EMA had moved meaningfully away from the bootstrap
value. At ~70-sample EMA half-life, a 100-sample floor means the EMA
has been refined through several half-lives before it can affect
dropping decisions.

Updates the dropper tests that previously used RequestSamples=50 to
use peerstats.MinLatencySamples so they stay robust to future value
changes. Design notes and a test comment reference the new value.
This commit is contained in:
Csaba Kiraly 2026-04-16 00:23:13 +02:00
parent 06c5ce8372
commit b178ec9a4a
3 changed files with 7 additions and 7 deletions

View file

@ -243,15 +243,15 @@ func TestProtectedByPoolRequestLatencyBasic(t *testing.T) {
// Three peers have enough samples; the two fastest should win.
stats[dialed[0].ID().String()] = peerstats.PeerStats{
RequestLatencyEMA: 50 * time.Millisecond,
RequestSamples: 50,
RequestSamples: peerstats.MinLatencySamples,
}
stats[dialed[1].ID().String()] = peerstats.PeerStats{
RequestLatencyEMA: 100 * time.Millisecond,
RequestSamples: 50,
RequestSamples: peerstats.MinLatencySamples,
}
stats[dialed[2].ID().String()] = peerstats.PeerStats{
RequestLatencyEMA: 2 * time.Second,
RequestSamples: 50,
RequestSamples: peerstats.MinLatencySamples,
}
protected := protectedPeersByPool(nil, dialed, stats)
@ -313,7 +313,7 @@ func TestProtectedByPoolRequestLatencyPerPool(t *testing.T) {
for _, p := range inbound {
stats[p.ID().String()] = peerstats.PeerStats{
RequestLatencyEMA: 50 * time.Millisecond,
RequestSamples: 50,
RequestSamples: peerstats.MinLatencySamples,
}
}
// Dialed peers are slower (1s) — globally they would all lose, but
@ -321,7 +321,7 @@ func TestProtectedByPoolRequestLatencyPerPool(t *testing.T) {
for _, p := range dialed {
stats[p.ID().String()] = peerstats.PeerStats{
RequestLatencyEMA: 1 * time.Second,
RequestSamples: 50,
RequestSamples: peerstats.MinLatencySamples,
}
}

View file

@ -52,7 +52,7 @@ const (
// MinLatencySamples is the number of latency samples a peer must accumulate
// before its RequestLatencyEMA is considered meaningful for protection.
// Prevents a single lucky-fast reply from displacing established peers.
MinLatencySamples = 10
MinLatencySamples = 100
)
// PeerStats is the exported per-peer snapshot returned by GetAllPeerStats.

View file

@ -181,7 +181,7 @@ func TestNotifyPeerDropClearsStats(t *testing.T) {
// TestStaleRequestLatencyAfterDrop documents the accepted behavior: a
// late sample after NotifyPeerDrop recreates a 1-sample entry. The
// dropper's MinLatencySamples=10 guard ensures this is harmless.
// dropper's MinLatencySamples=100 guard ensures this is harmless.
func TestStaleRequestLatencyAfterDrop(t *testing.T) {
s := New()
s.NotifyRequestLatency("peerA", 200*time.Millisecond)