mirror of https://github.com/status-im/go-waku.git
feat: add serviceSlot ds (#684)
* feat: add serviceSlot ds * test: service slot * nit: codeClimate related changs * nit: based on comments * nit: dont' run getPeers for WakuRelayIDv200
This commit is contained in:
parent
4a546d12d3
commit
accd9ff3e3
|
@ -1,3 +1,4 @@
|
||||||
|
.codeclimate.yml
|
||||||
nodekey
|
nodekey
|
||||||
rlnKeystore.json
|
rlnKeystore.json
|
||||||
test_onchain.json
|
test_onchain.json
|
||||||
|
|
|
@ -24,47 +24,49 @@ const WakuRelayIDv200 = protocol.ID("/vac/waku/relay/2.0.0")
|
||||||
// PeerManager applies various controls and manage connections towards peers.
|
// PeerManager applies various controls and manage connections towards peers.
|
||||||
type PeerManager struct {
|
type PeerManager struct {
|
||||||
peerConnector *PeerConnectionStrategy
|
peerConnector *PeerConnectionStrategy
|
||||||
maxConnections int
|
|
||||||
maxRelayPeers int
|
maxRelayPeers int
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
InRelayPeersTarget int
|
InRelayPeersTarget int
|
||||||
OutRelayPeersTarget int
|
OutRelayPeersTarget int
|
||||||
host host.Host
|
host host.Host
|
||||||
serviceSlots map[protocol.ID][]peer.ID
|
serviceSlots *ServiceSlots
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxRelayPeersShare = 5
|
|
||||||
|
|
||||||
// const defaultMaxOutRelayPeersTarget = 10
|
|
||||||
const outRelayPeersShare = 3
|
|
||||||
const peerConnectivityLoopSecs = 15
|
const peerConnectivityLoopSecs = 15
|
||||||
const minOutRelayConns = 10
|
|
||||||
|
// 80% relay peers 20% service peers
|
||||||
|
func relayAndServicePeers(maxConnections int) (int, int) {
|
||||||
|
return maxConnections - maxConnections/5, maxConnections / 5
|
||||||
|
}
|
||||||
|
|
||||||
|
// 66% inRelayPeers 33% outRelayPeers
|
||||||
|
func inAndOutRelayPeers(relayPeers int) (int, int) {
|
||||||
|
outRelayPeers := relayPeers / 3
|
||||||
|
//
|
||||||
|
const minOutRelayConns = 10
|
||||||
|
if outRelayPeers < minOutRelayConns {
|
||||||
|
outRelayPeers = minOutRelayConns
|
||||||
|
}
|
||||||
|
return relayPeers - outRelayPeers, outRelayPeers
|
||||||
|
}
|
||||||
|
|
||||||
// NewPeerManager creates a new peerManager instance.
|
// NewPeerManager creates a new peerManager instance.
|
||||||
func NewPeerManager(maxConnections int, logger *zap.Logger) *PeerManager {
|
func NewPeerManager(maxConnections int, logger *zap.Logger) *PeerManager {
|
||||||
|
|
||||||
maxRelayPeersValue := maxConnections - (maxConnections / maxRelayPeersShare)
|
maxRelayPeers, _ := relayAndServicePeers(maxConnections)
|
||||||
outRelayPeersTargetValue := int(maxRelayPeersValue / outRelayPeersShare)
|
inRelayPeersTarget, outRelayPeersTarget := inAndOutRelayPeers(maxRelayPeers)
|
||||||
if outRelayPeersTargetValue < minOutRelayConns {
|
|
||||||
outRelayPeersTargetValue = minOutRelayConns
|
|
||||||
}
|
|
||||||
inRelayPeersTargetValue := maxRelayPeersValue - outRelayPeersTargetValue
|
|
||||||
if inRelayPeersTargetValue < 0 {
|
|
||||||
inRelayPeersTargetValue = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pm := &PeerManager{
|
pm := &PeerManager{
|
||||||
maxConnections: maxConnections,
|
|
||||||
logger: logger.Named("peer-manager"),
|
logger: logger.Named("peer-manager"),
|
||||||
maxRelayPeers: maxRelayPeersValue,
|
maxRelayPeers: maxRelayPeers,
|
||||||
InRelayPeersTarget: inRelayPeersTargetValue,
|
InRelayPeersTarget: inRelayPeersTarget,
|
||||||
OutRelayPeersTarget: outRelayPeersTargetValue,
|
OutRelayPeersTarget: outRelayPeersTarget,
|
||||||
serviceSlots: make(map[protocol.ID][]peer.ID),
|
serviceSlots: NewServiceSlot(),
|
||||||
}
|
}
|
||||||
logger.Info("PeerManager init values", zap.Int("maxConnections", maxConnections),
|
logger.Info("PeerManager init values", zap.Int("maxConnections", maxConnections),
|
||||||
zap.Int("maxRelayPeersValue", maxRelayPeersValue),
|
zap.Int("maxRelayPeers", maxRelayPeers),
|
||||||
zap.Int("outRelayPeersTargetValue", outRelayPeersTargetValue),
|
zap.Int("outRelayPeersTarget", outRelayPeersTarget),
|
||||||
zap.Int("inRelayPeersTarget", pm.InRelayPeersTarget))
|
zap.Int("inRelayPeersTarget", pm.InRelayPeersTarget))
|
||||||
|
|
||||||
return pm
|
return pm
|
||||||
|
@ -274,13 +276,7 @@ func (pm *PeerManager) RemovePeer(peerID peer.ID) {
|
||||||
pm.host.Peerstore().RemovePeer(peerID)
|
pm.host.Peerstore().RemovePeer(peerID)
|
||||||
//Search if this peer is in serviceSlot and if so, remove it from there
|
//Search if this peer is in serviceSlot and if so, remove it from there
|
||||||
// TODO:Add another peer which is statically configured to the serviceSlot.
|
// TODO:Add another peer which is statically configured to the serviceSlot.
|
||||||
for proto, peers := range pm.serviceSlots {
|
pm.serviceSlots.removePeer(peerID)
|
||||||
for i, peer := range peers {
|
|
||||||
if peer == peerID {
|
|
||||||
pm.serviceSlots[proto][i] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPeerToServiceSlot adds a peerID to serviceSlot.
|
// AddPeerToServiceSlot adds a peerID to serviceSlot.
|
||||||
|
@ -296,7 +292,8 @@ func (pm *PeerManager) AddPeerToServiceSlot(proto protocol.ID, peerID peer.ID) {
|
||||||
//TODO: Ideally we should sort the peers per service and return best peer based on peer score or RTT etc.
|
//TODO: Ideally we should sort the peers per service and return best peer based on peer score or RTT etc.
|
||||||
pm.logger.Info("Adding peer to service slots", logging.HostID("peer", peerID),
|
pm.logger.Info("Adding peer to service slots", logging.HostID("peer", peerID),
|
||||||
zap.String("service", string(proto)))
|
zap.String("service", string(proto)))
|
||||||
pm.serviceSlots[proto] = append(pm.serviceSlots[proto], peerID)
|
// getPeers returns nil for WakuRelayIDv200 protocol, but we don't run this ServiceSlot code for WakuRelayIDv200 protocol
|
||||||
|
pm.serviceSlots.getPeers(proto).add(peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectPeer is used to return a random peer that supports a given protocol.
|
// SelectPeer is used to return a random peer that supports a given protocol.
|
||||||
|
@ -310,19 +307,17 @@ func (pm *PeerManager) SelectPeer(proto protocol.ID, specificPeers []peer.ID, lo
|
||||||
// - which topics they track
|
// - which topics they track
|
||||||
// - latency?
|
// - latency?
|
||||||
|
|
||||||
|
//Try to fetch from serviceSlot
|
||||||
|
if slot := pm.serviceSlots.getPeers(proto); slot != nil {
|
||||||
|
if peerID, err := slot.getRandom(); err == nil {
|
||||||
|
return peerID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if not found in serviceSlots or proto == WakuRelayIDv200
|
||||||
filteredPeers, err := utils.FilterPeersByProto(pm.host, specificPeers, proto)
|
filteredPeers, err := utils.FilterPeersByProto(pm.host, specificPeers, proto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if proto == WakuRelayIDv200 {
|
|
||||||
return utils.SelectRandomPeer(filteredPeers, pm.logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Try to fetch from serviceSlot
|
|
||||||
peerIDs, ok := pm.serviceSlots[proto]
|
|
||||||
if ok || len(peerIDs) > 0 {
|
|
||||||
filteredPeers = peerIDs
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils.SelectRandomPeer(filteredPeers, pm.logger)
|
return utils.SelectRandomPeer(filteredPeers, pm.logger)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package peermanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peerMap struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
m map[peer.ID]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPeerMap() *peerMap {
|
||||||
|
return &peerMap{
|
||||||
|
m: map[peer.ID]struct{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *peerMap) getRandom() (peer.ID, error) {
|
||||||
|
pm.mu.RLock()
|
||||||
|
defer pm.mu.RUnlock()
|
||||||
|
for pID := range pm.m {
|
||||||
|
return pID, nil
|
||||||
|
}
|
||||||
|
return "", utils.ErrNoPeersAvailable
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pm *peerMap) remove(pID peer.ID) {
|
||||||
|
pm.mu.Lock()
|
||||||
|
defer pm.mu.Unlock()
|
||||||
|
|
||||||
|
delete(pm.m, pID)
|
||||||
|
}
|
||||||
|
func (pm *peerMap) add(pID peer.ID) {
|
||||||
|
pm.mu.Lock()
|
||||||
|
defer pm.mu.Unlock()
|
||||||
|
pm.m[pID] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceSlots is for storing service slots for a given protocol topic
|
||||||
|
type ServiceSlots struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
m map[protocol.ID]*peerMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServiceSlot is a constructor for ServiceSlot
|
||||||
|
func NewServiceSlot() *ServiceSlots {
|
||||||
|
return &ServiceSlots{
|
||||||
|
m: map[protocol.ID]*peerMap{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPeers for getting all the peers for a given protocol
|
||||||
|
// since peerMap is only used in peerManager that's why it is unexported
|
||||||
|
func (slots *ServiceSlots) getPeers(proto protocol.ID) *peerMap {
|
||||||
|
if proto == WakuRelayIDv200 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
slots.mu.Lock()
|
||||||
|
defer slots.mu.Unlock()
|
||||||
|
if slots.m[proto] == nil {
|
||||||
|
slots.m[proto] = newPeerMap()
|
||||||
|
}
|
||||||
|
return slots.m[proto]
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemovePeer for removing peer ID for a given protocol
|
||||||
|
func (slots *ServiceSlots) removePeer(peerID peer.ID) {
|
||||||
|
slots.mu.Lock()
|
||||||
|
defer slots.mu.Unlock()
|
||||||
|
for _, m := range slots.m {
|
||||||
|
m.remove(peerID)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package peermanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
libp2pProtocol "github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/waku-org/go-waku/waku/v2/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServiceSlot(t *testing.T) {
|
||||||
|
slots := NewServiceSlot()
|
||||||
|
|
||||||
|
protocol := libp2pProtocol.ID("test/protocol")
|
||||||
|
|
||||||
|
peerId := peer.ID("peerId")
|
||||||
|
|
||||||
|
//
|
||||||
|
slots.getPeers(protocol).add(peerId)
|
||||||
|
//
|
||||||
|
fetchedPeer, err := slots.getPeers(protocol).getRandom()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, peerId, fetchedPeer)
|
||||||
|
|
||||||
|
//
|
||||||
|
slots.getPeers(protocol).remove(peerId)
|
||||||
|
//
|
||||||
|
_, err = slots.getPeers(protocol).getRandom()
|
||||||
|
require.Equal(t, err, utils.ErrNoPeersAvailable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServiceSlotRemovePeerFromAll(t *testing.T) {
|
||||||
|
slots := NewServiceSlot()
|
||||||
|
|
||||||
|
protocol := libp2pProtocol.ID("test/protocol")
|
||||||
|
protocol1 := libp2pProtocol.ID("test/protocol1")
|
||||||
|
|
||||||
|
peerId := peer.ID("peerId")
|
||||||
|
|
||||||
|
//
|
||||||
|
slots.getPeers(protocol).add(peerId)
|
||||||
|
slots.getPeers(protocol1).add(peerId)
|
||||||
|
//
|
||||||
|
fetchedPeer, err := slots.getPeers(protocol1).getRandom()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, peerId, fetchedPeer)
|
||||||
|
|
||||||
|
//
|
||||||
|
slots.removePeer(peerId)
|
||||||
|
//
|
||||||
|
_, err = slots.getPeers(protocol).getRandom()
|
||||||
|
require.Equal(t, err, utils.ErrNoPeersAvailable)
|
||||||
|
_, err = slots.getPeers(protocol1).getRandom()
|
||||||
|
require.Equal(t, err, utils.ErrNoPeersAvailable)
|
||||||
|
}
|
Loading…
Reference in New Issue