From 7965e743aa40f1f9322281cc975743e1a21446e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 6 Apr 2017 20:05:32 +0300 Subject: [PATCH] p2p: add a 20 second warmup interval before dialing bootnodes --- p2p/dial.go | 14 ++++++++++++-- p2p/dial_test.go | 20 ++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/p2p/dial.go b/p2p/dial.go index 28faed1b8f..b779713963 100644 --- a/p2p/dial.go +++ b/p2p/dial.go @@ -38,6 +38,10 @@ const ( // once every few seconds. lookupInterval = 4 * time.Second + // If no peers are found for this amount of time, the initial bootnodes are + // attempted to be connected. + fallbackInterval = 20 * time.Second + // Endpoint resolution is throttled with bounded backoff. initialResolveDelay = 60 * time.Second maxResolveDelay = time.Hour @@ -53,11 +57,13 @@ type dialstate struct { lookupRunning bool dialing map[discover.NodeID]connFlag - bootnodes []*discover.Node // default dials when there are no peers lookupBuf []*discover.Node // current discovery lookup results randomNodes []*discover.Node // filled from Table static map[discover.NodeID]*dialTask hist *dialHistory + + start time.Time // time when the dialer was first used + bootnodes []*discover.Node // default dials when there are no peers } type discoverTable interface { @@ -133,6 +139,10 @@ func (s *dialstate) removeStatic(n *discover.Node) { } func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task { + if s.start == (time.Time{}) { + s.start = now + } + var newtasks []task addDial := func(flag connFlag, n *discover.Node) bool { if err := s.checkDial(n, peers); err != nil { @@ -175,7 +185,7 @@ func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now // If we don't have any peers whatsoever, try to dial a random bootnode. This // scenario is useful for the testnet (and private networks) where the discovery // table might be full of mostly bad peers, making it hard to find good ones. - if len(peers) == 0 && len(s.bootnodes) > 0 && needDynDials > 0 { + if len(peers) == 0 && len(s.bootnodes) > 0 && needDynDials > 0 && now.Sub(s.start) > fallbackInterval { bootnode := s.bootnodes[0] s.bootnodes = append(s.bootnodes[:0], s.bootnodes[1:]...) s.bootnodes = append(s.bootnodes, bootnode) diff --git a/p2p/dial_test.go b/p2p/dial_test.go index 6c64b323ea..08e863bae0 100644 --- a/p2p/dial_test.go +++ b/p2p/dial_test.go @@ -236,13 +236,29 @@ func TestDialStateDynDialBootnode(t *testing.T) { runDialTest(t, dialtest{ init: newDialState(nil, bootnodes, table, 5, nil), rounds: []round{ - // 1 out of 3 bootnodes are dialed on no peers, 2 other dynamic dials + // 2 dynamic dials attempted, bootnodes pending fallback interval + { + new: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + &discoverTask{}, + }, + }, + // No dials succeed, bootnodes still pending fallback interval + { + done: []task{ + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, + &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, + }, + }, + // No dials succeed, bootnodes still pending fallback interval + {}, + // No dials succeed, 2 dynamic dials attempted and 1 bootnode too as fallback interval was reached { new: []task{ &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(1)}}, &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(4)}}, &dialTask{flags: dynDialedConn, dest: &discover.Node{ID: uintID(5)}}, - &discoverTask{}, }, }, // No dials succeed, 2nd bootnode is attempted