peerresponsetracker.go 1.77 KB
Newer Older
dirkmc's avatar
dirkmc committed
1 2 3 4 5
package session

import (
	"math/rand"

6
	peer "gitlab.dms3.io/p2p/go-p2p-core/peer"
dirkmc's avatar
dirkmc committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20
)

// peerResponseTracker keeps track of how many times each peer was the first
// to send us a block for a given CID (used to rank peers)
type peerResponseTracker struct {
	firstResponder map[peer.ID]int
}

func newPeerResponseTracker() *peerResponseTracker {
	return &peerResponseTracker{
		firstResponder: make(map[peer.ID]int),
	}
}

Dirk McCormick's avatar
Dirk McCormick committed
21 22
// receivedBlockFrom is called when a block is received from a peer
// (only called first time block is received)
dirkmc's avatar
dirkmc committed
23 24 25 26
func (prt *peerResponseTracker) receivedBlockFrom(from peer.ID) {
	prt.firstResponder[from]++
}

Dirk McCormick's avatar
Dirk McCormick committed
27 28
// choose picks a peer from the list of candidate peers, favouring those peers
// that were first to send us previous blocks
dirkmc's avatar
dirkmc committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
func (prt *peerResponseTracker) choose(peers []peer.ID) peer.ID {
	if len(peers) == 0 {
		return ""
	}

	rnd := rand.Float64()

	// Find the total received blocks for all candidate peers
	total := 0
	for _, p := range peers {
		total += prt.getPeerCount(p)
	}

	// Choose one of the peers with a chance proportional to the number
	// of blocks received from that peer
	counted := 0.0
	for _, p := range peers {
		counted += float64(prt.getPeerCount(p)) / float64(total)
		if counted > rnd {
			return p
		}
	}

	// We shouldn't get here unless there is some weirdness with floating point
	// math that doesn't quite cover the whole range of peers in the for loop
	// so just choose the last peer.
	index := len(peers) - 1
	return peers[index]
}

Dirk McCormick's avatar
Dirk McCormick committed
59 60
// getPeerCount returns the number of times the peer was first to send us a
// block
dirkmc's avatar
dirkmc committed
61 62 63 64 65 66 67 68 69 70
func (prt *peerResponseTracker) getPeerCount(p peer.ID) int {
	count, ok := prt.firstResponder[p]
	if ok {
		return count
	}

	// Make sure there is always at least a small chance a new peer
	// will be chosen
	return 1
}