From 6fb810adaa539a2fa97cf4481588b339ab5279ae Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Mon, 22 Jun 2015 00:01:56 +0200
Subject: [PATCH] p2p: throttle all discovery lookups

Lookup calls would spin out of control when network connectivity was
lost. The throttling that was in place only took effect when the table
returned zero results, which doesn't happen very often.

The new throttling should not have a negative impact when the host is
online. Lookups against the network take some time and dials for all
results must complete or hit the cache before a new one is started. This
usually takes longer than four seconds, leaving online lookups
unaffected.

Fixes #1296
---
 p2p/dial.go   | 30 +++++++++++++++---------------
 p2p/server.go |  1 +
 2 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/p2p/dial.go b/p2p/dial.go
index 71065c5ee..b82d6d1f5 100644
--- a/p2p/dial.go
+++ b/p2p/dial.go
@@ -17,10 +17,9 @@ const (
 	// redialing a certain node.
 	dialHistoryExpiration = 30 * time.Second
 
-	// Discovery lookup tasks will wait for this long when
-	// no results are returned. This can happen if the table
-	// becomes empty (i.e. not often).
-	emptyLookupDelay = 10 * time.Second
+	// Discovery lookups are throttled and can only run
+	// once every few seconds.
+	lookupInterval = 4 * time.Second
 )
 
 // dialstate schedules dials and discovery lookups.
@@ -206,18 +205,19 @@ func (t *dialTask) String() string {
 func (t *discoverTask) Do(srv *Server) {
 	if t.bootstrap {
 		srv.ntab.Bootstrap(srv.BootstrapNodes)
-	} else {
-		var target discover.NodeID
-		rand.Read(target[:])
-		t.results = srv.ntab.Lookup(target)
-		// newTasks generates a lookup task whenever dynamic dials are
-		// necessary. Lookups need to take some time, otherwise the
-		// event loop spins too fast. An empty result can only be
-		// returned if the table is empty.
-		if len(t.results) == 0 {
-			time.Sleep(emptyLookupDelay)
-		}
+		return
 	}
+	// newTasks generates a lookup task whenever dynamic dials are
+	// necessary. Lookups need to take some time, otherwise the
+	// event loop spins too fast.
+	next := srv.lastLookup.Add(lookupInterval)
+	if now := time.Now(); now.Before(next) {
+		time.Sleep(next.Sub(now))
+	}
+	srv.lastLookup = time.Now()
+	var target discover.NodeID
+	rand.Read(target[:])
+	t.results = srv.ntab.Lookup(target)
 }
 
 func (t *discoverTask) String() (s string) {
diff --git a/p2p/server.go b/p2p/server.go
index 59b97a0aa..5eff70345 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -115,6 +115,7 @@ type Server struct {
 	ntab         discoverTable
 	listener     net.Listener
 	ourHandshake *protoHandshake
+	lastLookup   time.Time
 
 	// These are for Peers, PeerCount (and nothing else).
 	peerOp     chan peerOpFunc