mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
p2p/netutil: GC also when adding statements and contacts
This prevents indefinite memory growth if Predict* isn't called.
This commit is contained in:
parent
b4a7d56fe3
commit
05f55dc0af
2 changed files with 49 additions and 8 deletions
|
|
@ -25,12 +25,14 @@ import (
|
|||
// IPTracker predicts the external endpoint, i.e. IP address and port, of the local host
|
||||
// based on statements made by other hosts.
|
||||
type IPTracker struct {
|
||||
window time.Duration
|
||||
contactWindow time.Duration
|
||||
minStatements int
|
||||
clock mclock.Clock
|
||||
statements map[string]ipStatement
|
||||
contact map[string]mclock.AbsTime
|
||||
window time.Duration
|
||||
contactWindow time.Duration
|
||||
minStatements int
|
||||
clock mclock.Clock
|
||||
statements map[string]ipStatement
|
||||
contact map[string]mclock.AbsTime
|
||||
lastStatementGC mclock.AbsTime
|
||||
lastContactGC mclock.AbsTime
|
||||
}
|
||||
|
||||
type ipStatement struct {
|
||||
|
|
@ -90,16 +92,25 @@ func (it *IPTracker) PredictEndpoint() string {
|
|||
|
||||
// AddStatement records that a certain host thinks our external endpoint is the one given.
|
||||
func (it *IPTracker) AddStatement(host, endpoint string) {
|
||||
it.statements[host] = ipStatement{endpoint, it.clock.Now()}
|
||||
now := it.clock.Now()
|
||||
it.statements[host] = ipStatement{endpoint, now}
|
||||
if time.Duration(now-it.lastStatementGC) >= it.window {
|
||||
it.gcStatements(now)
|
||||
}
|
||||
}
|
||||
|
||||
// AddContact records that a packet containing our endpoint information has been sent to a
|
||||
// certain host.
|
||||
func (it *IPTracker) AddContact(host string) {
|
||||
it.contact[host] = it.clock.Now()
|
||||
now := it.clock.Now()
|
||||
it.contact[host] = now
|
||||
if time.Duration(now-it.lastContactGC) >= it.contactWindow {
|
||||
it.gcContact(now)
|
||||
}
|
||||
}
|
||||
|
||||
func (it *IPTracker) gcStatements(now mclock.AbsTime) {
|
||||
it.lastStatementGC = now
|
||||
cutoff := now.Add(-it.window)
|
||||
for host, s := range it.statements {
|
||||
if s.time < cutoff {
|
||||
|
|
@ -109,6 +120,7 @@ func (it *IPTracker) gcStatements(now mclock.AbsTime) {
|
|||
}
|
||||
|
||||
func (it *IPTracker) gcContact(now mclock.AbsTime) {
|
||||
it.lastContactGC = now
|
||||
cutoff := now.Add(-it.contactWindow)
|
||||
for host, ct := range it.contact {
|
||||
if ct < cutoff {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package netutil
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
mrand "math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -107,3 +108,31 @@ func runIPTrackerTest(t *testing.T, evs []iptrackTestEvent) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This checks that old statements and contacts are GCed even if Predict* isn't called.
|
||||
func TestIPTrackerForceGC(t *testing.T) {
|
||||
var (
|
||||
clock mclock.Simulated
|
||||
window = 10 * time.Second
|
||||
rate = 50 * time.Millisecond
|
||||
max = int(window/rate) + 1
|
||||
it = NewIPTracker(window, window, 3)
|
||||
)
|
||||
it.clock = &clock
|
||||
|
||||
for i := 0; i < 5*max; i++ {
|
||||
e1 := make([]byte, 4)
|
||||
e2 := make([]byte, 4)
|
||||
mrand.Read(e1)
|
||||
mrand.Read(e2)
|
||||
it.AddStatement(string(e1), string(e2))
|
||||
it.AddContact(string(e1))
|
||||
clock.Run(rate)
|
||||
}
|
||||
if len(it.contact) > 2*max {
|
||||
t.Errorf("contacts not GCed, have %d", len(it.contact))
|
||||
}
|
||||
if len(it.statements) > 2*max {
|
||||
t.Errorf("statements not GCed, have %d", len(it.statements))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue