p2p/netutil: GC also when adding statements and contacts

This prevents indefinite memory growth if Predict* isn't called.
This commit is contained in:
Felix Lange 2018-10-04 14:29:05 +02:00
parent b4a7d56fe3
commit 05f55dc0af
2 changed files with 49 additions and 8 deletions

View file

@ -31,6 +31,8 @@ type IPTracker struct {
clock mclock.Clock clock mclock.Clock
statements map[string]ipStatement statements map[string]ipStatement
contact map[string]mclock.AbsTime contact map[string]mclock.AbsTime
lastStatementGC mclock.AbsTime
lastContactGC mclock.AbsTime
} }
type ipStatement struct { 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. // AddStatement records that a certain host thinks our external endpoint is the one given.
func (it *IPTracker) AddStatement(host, endpoint string) { 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 // AddContact records that a packet containing our endpoint information has been sent to a
// certain host. // certain host.
func (it *IPTracker) AddContact(host string) { 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) { func (it *IPTracker) gcStatements(now mclock.AbsTime) {
it.lastStatementGC = now
cutoff := now.Add(-it.window) cutoff := now.Add(-it.window)
for host, s := range it.statements { for host, s := range it.statements {
if s.time < cutoff { if s.time < cutoff {
@ -109,6 +120,7 @@ func (it *IPTracker) gcStatements(now mclock.AbsTime) {
} }
func (it *IPTracker) gcContact(now mclock.AbsTime) { func (it *IPTracker) gcContact(now mclock.AbsTime) {
it.lastContactGC = now
cutoff := now.Add(-it.contactWindow) cutoff := now.Add(-it.contactWindow)
for host, ct := range it.contact { for host, ct := range it.contact {
if ct < cutoff { if ct < cutoff {

View file

@ -18,6 +18,7 @@ package netutil
import ( import (
"fmt" "fmt"
mrand "math/rand"
"testing" "testing"
"time" "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))
}
}