p2p/discover: don't attempt to replace nodes that are being replaced

PR #1621 changed Table locking so the mutex is not held while a
contested node is being pinged. If multiple nodes ping the local node
during this time window, multiple ping packets will be sent to the
contested node. The changes in this commit prevent multiple packets by
tracking whether the node is being replaced.
This commit is contained in:
Felix Lange 2015-08-17 11:27:41 +02:00
parent edccc7ae34
commit dd54fef898
2 changed files with 15 additions and 4 deletions

View File

@ -48,6 +48,10 @@ type Node struct {
// In those tests, the content of sha will not actually correspond // In those tests, the content of sha will not actually correspond
// with ID. // with ID.
sha common.Hash sha common.Hash
// whether this node is currently being pinged in order to replace
// it in a bucket
contested bool
} }
func newNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node { func newNode(id NodeID, ip net.IP, udpPort, tcpPort uint16) *Node {

View File

@ -455,24 +455,31 @@ func (tab *Table) ping(id NodeID, addr *net.UDPAddr) error {
func (tab *Table) add(new *Node) { func (tab *Table) add(new *Node) {
b := tab.buckets[logdist(tab.self.sha, new.sha)] b := tab.buckets[logdist(tab.self.sha, new.sha)]
tab.mutex.Lock() tab.mutex.Lock()
defer tab.mutex.Unlock()
if b.bump(new) { if b.bump(new) {
tab.mutex.Unlock()
return return
} }
var oldest *Node var oldest *Node
if len(b.entries) == bucketSize { if len(b.entries) == bucketSize {
oldest = b.entries[bucketSize-1] oldest = b.entries[bucketSize-1]
if oldest.contested {
// The node is already being replaced, don't attempt
// to replace it.
return
}
oldest.contested = true
// Let go of the mutex so other goroutines can access // Let go of the mutex so other goroutines can access
// the table while we ping the least recently active node. // the table while we ping the least recently active node.
tab.mutex.Unlock() tab.mutex.Unlock()
if err := tab.ping(oldest.ID, oldest.addr()); err == nil { err := tab.ping(oldest.ID, oldest.addr())
tab.mutex.Lock()
oldest.contested = false
if err == nil {
// The node responded, don't replace it. // The node responded, don't replace it.
return return
} }
tab.mutex.Lock()
} }
added := b.replace(new, oldest) added := b.replace(new, oldest)
tab.mutex.Unlock()
if added && tab.nodeAddedHook != nil { if added && tab.nodeAddedHook != nil {
tab.nodeAddedHook(new) tab.nodeAddedHook(new)
} }