Node API allows us to drop all network locks
Network locks are now needed only when accessing client map. node map access is serialied with the node mutex.
This commit is contained in:
parent
ef91d836eb
commit
b91c3147e7
@ -124,9 +124,10 @@ func newNetwork(opts ...Option) Network {
|
|||||||
|
|
||||||
// Options returns network options
|
// Options returns network options
|
||||||
func (n *network) Options() Options {
|
func (n *network) Options() Options {
|
||||||
n.Lock()
|
n.RLock()
|
||||||
|
defer n.RUnlock()
|
||||||
|
|
||||||
options := n.options
|
options := n.options
|
||||||
n.Unlock()
|
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
@ -196,7 +197,6 @@ func (n *network) handleNetConn(sess tunnel.Session, msg chan *transport.Message
|
|||||||
for {
|
for {
|
||||||
m := new(transport.Message)
|
m := new(transport.Message)
|
||||||
if err := sess.Recv(m); err != nil {
|
if err := sess.Recv(m); err != nil {
|
||||||
// TODO: should we bail here?
|
|
||||||
log.Debugf("Network tunnel [%s] receive error: %v", NetworkChannel, err)
|
log.Debugf("Network tunnel [%s] receive error: %v", NetworkChannel, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -255,28 +255,23 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen
|
|||||||
if pbNetConnect.Node.Id == n.options.Id {
|
if pbNetConnect.Node.Id == n.options.Id {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
n.Lock()
|
|
||||||
log.Debugf("Network received connect message from: %s", pbNetConnect.Node.Id)
|
log.Debugf("Network received connect message from: %s", pbNetConnect.Node.Id)
|
||||||
// if the entry already exists skip adding it
|
peer := &node{
|
||||||
if peer, ok := n.peers[pbNetConnect.Node.Id]; ok {
|
|
||||||
// update lastSeen timestamp
|
|
||||||
if n.peers[pbNetConnect.Node.Id].lastSeen.Before(now) {
|
|
||||||
peer.lastSeen = now
|
|
||||||
}
|
|
||||||
n.Unlock()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// add a new peer to the node peers
|
|
||||||
// NOTE: new node does not have any peers, yet
|
|
||||||
n.peers[pbNetConnect.Node.Id] = &node{
|
|
||||||
id: pbNetConnect.Node.Id,
|
id: pbNetConnect.Node.Id,
|
||||||
address: pbNetConnect.Node.Address,
|
address: pbNetConnect.Node.Address,
|
||||||
peers: make(map[string]*node),
|
peers: make(map[string]*node),
|
||||||
lastSeen: now,
|
lastSeen: now,
|
||||||
}
|
}
|
||||||
n.Unlock()
|
if ok := n.node.AddPeer(peer); !ok {
|
||||||
// get all the node peers down to MaxDepth encoded in protobuf
|
log.Debugf("Network peer exists, refreshing: %s", peer.id)
|
||||||
msg := PeersToProto(n.node, n.Peers(), MaxDepth)
|
// update lastSeen time for the peer
|
||||||
|
if ok := n.RefreshPeer(peer.id, now); !ok {
|
||||||
|
log.Debugf("Network failed refreshing peer: %s", peer.id)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// get node peers down to MaxDepth encoded in protobuf
|
||||||
|
msg := PeersToProto(n.node, MaxDepth)
|
||||||
// advertise yourself to the network
|
// advertise yourself to the network
|
||||||
if err := n.sendMsg("peer", msg, NetworkChannel); err != nil {
|
if err := n.sendMsg("peer", msg, NetworkChannel); err != nil {
|
||||||
log.Debugf("Network failed to advertise peers: %v", err)
|
log.Debugf("Network failed to advertise peers: %v", err)
|
||||||
@ -297,18 +292,14 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen
|
|||||||
if pbNetPeer.Node.Id == n.options.Id {
|
if pbNetPeer.Node.Id == n.options.Id {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
n.Lock()
|
|
||||||
log.Debugf("Network received peer message from: %s", pbNetPeer.Node.Id)
|
log.Debugf("Network received peer message from: %s", pbNetPeer.Node.Id)
|
||||||
// only add the peer if it is NOT already in node's list of peers
|
peer := &node{
|
||||||
_, exists := n.peers[pbNetPeer.Node.Id]
|
id: pbNetPeer.Node.Id,
|
||||||
if !exists {
|
address: pbNetPeer.Node.Address,
|
||||||
n.peers[pbNetPeer.Node.Id] = &node{
|
peers: make(map[string]*node),
|
||||||
id: pbNetPeer.Node.Id,
|
lastSeen: now,
|
||||||
address: pbNetPeer.Node.Address,
|
}
|
||||||
peers: make(map[string]*node),
|
if ok := n.node.AddPeer(peer); ok {
|
||||||
lastSeen: now,
|
|
||||||
}
|
|
||||||
n.Unlock()
|
|
||||||
// send a solicit message when discovering new peer
|
// send a solicit message when discovering new peer
|
||||||
msg := &pbRtr.Solicit{
|
msg := &pbRtr.Solicit{
|
||||||
Id: n.options.Id,
|
Id: n.options.Id,
|
||||||
@ -316,18 +307,19 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen
|
|||||||
if err := n.sendMsg("solicit", msg, ControlChannel); err != nil {
|
if err := n.sendMsg("solicit", msg, ControlChannel); err != nil {
|
||||||
log.Debugf("Network failed to send solicit message: %s", err)
|
log.Debugf("Network failed to send solicit message: %s", err)
|
||||||
}
|
}
|
||||||
// after adding new peer go to the next step
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// NOTE: we don't update MaxDepth toplogy as we dont update this node only its peers
|
log.Debugf("Network peer exists, refreshing: %s", pbNetPeer.Node.Id)
|
||||||
if err := n.node.updatePeerTopology(pbNetPeer, MaxDepth-1); err != nil {
|
// update lastSeen time for the peer
|
||||||
|
if ok := n.RefreshPeer(pbNetPeer.Node.Id, now); !ok {
|
||||||
|
log.Debugf("Network failed refreshing peer: %s", pbNetPeer.Node.Id)
|
||||||
|
}
|
||||||
|
// NOTE: we don't uunpack MaxDepth toplogy
|
||||||
|
peer = UnpackPeerTopology(pbNetPeer, now, MaxDepth-1)
|
||||||
|
log.Debugf("Network updating topology of node: %s", n.node.id)
|
||||||
|
if ok := n.node.UpdatePeer(peer); !ok {
|
||||||
log.Debugf("Network failed to update peers")
|
log.Debugf("Network failed to update peers")
|
||||||
}
|
}
|
||||||
// update lastSeen timestamp if outdated
|
|
||||||
if n.peers[pbNetPeer.Node.Id].lastSeen.Before(now) {
|
|
||||||
n.peers[pbNetPeer.Node.Id].lastSeen = now
|
|
||||||
}
|
|
||||||
n.Unlock()
|
|
||||||
case "close":
|
case "close":
|
||||||
pbNetClose := &pbNet.Close{}
|
pbNetClose := &pbNet.Close{}
|
||||||
if err := proto.Unmarshal(m.Body, pbNetClose); err != nil {
|
if err := proto.Unmarshal(m.Body, pbNetClose); err != nil {
|
||||||
@ -338,13 +330,11 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen
|
|||||||
if pbNetClose.Node.Id == n.options.Id {
|
if pbNetClose.Node.Id == n.options.Id {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
n.Lock()
|
|
||||||
log.Debugf("Network received close message from: %s", pbNetClose.Node.Id)
|
log.Debugf("Network received close message from: %s", pbNetClose.Node.Id)
|
||||||
if err := n.pruneNode(pbNetClose.Node.Id); err != nil {
|
if err := n.pruneNode(pbNetClose.Node.Id); err != nil {
|
||||||
log.Debugf("Network failed to prune the node %s: %v", pbNetClose.Node.Id, err)
|
log.Debugf("Network failed to prune the node %s: %v", pbNetClose.Node.Id, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
n.Unlock()
|
|
||||||
}
|
}
|
||||||
case <-n.closed:
|
case <-n.closed:
|
||||||
return
|
return
|
||||||
@ -393,7 +383,7 @@ func (n *network) announce(client transport.Client) {
|
|||||||
case <-n.closed:
|
case <-n.closed:
|
||||||
return
|
return
|
||||||
case <-announce.C:
|
case <-announce.C:
|
||||||
msg := PeersToProto(n.node, n.Peers(), MaxDepth)
|
msg := PeersToProto(n.node, MaxDepth)
|
||||||
// advertise yourself to the network
|
// advertise yourself to the network
|
||||||
if err := n.sendMsg("peer", msg, NetworkChannel); err != nil {
|
if err := n.sendMsg("peer", msg, NetworkChannel); err != nil {
|
||||||
log.Debugf("Network failed to advertise peers: %v", err)
|
log.Debugf("Network failed to advertise peers: %v", err)
|
||||||
@ -404,9 +394,9 @@ func (n *network) announce(client transport.Client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pruneNode removes a node with given id from the list of peers. It also removes all routes originted by this node.
|
// pruneNode removes a node with given id from the list of peers. It also removes all routes originted by this node.
|
||||||
// NOTE: this method is not thread-safe; when calling it make sure you lock the particular code segment
|
|
||||||
func (n *network) pruneNode(id string) error {
|
func (n *network) pruneNode(id string) error {
|
||||||
delete(n.peers, id)
|
// DeletePeer serializes access
|
||||||
|
n.node.DeletePeer(id)
|
||||||
// lookup all the routes originated at this node
|
// lookup all the routes originated at this node
|
||||||
q := router.NewQuery(
|
q := router.NewQuery(
|
||||||
router.QueryRouter(id),
|
router.QueryRouter(id),
|
||||||
@ -506,11 +496,9 @@ func (n *network) setRouteMetric(route *router.Route) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
n.RLock()
|
|
||||||
// check if the route origin is our peer
|
// check if the route origin is our peer
|
||||||
if _, ok := n.peers[route.Router]; ok {
|
if _, ok := n.peers[route.Router]; ok {
|
||||||
route.Metric = 10
|
route.Metric = 10
|
||||||
n.RUnlock()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,12 +507,10 @@ func (n *network) setRouteMetric(route *router.Route) {
|
|||||||
for id := range peer.peers {
|
for id := range peer.peers {
|
||||||
if route.Router == id {
|
if route.Router == id {
|
||||||
route.Metric = 100
|
route.Metric = 100
|
||||||
n.RUnlock()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n.RUnlock()
|
|
||||||
|
|
||||||
// the origin of the route is beyond our neighbourhood
|
// the origin of the route is beyond our neighbourhood
|
||||||
route.Metric = 1000
|
route.Metric = 1000
|
||||||
@ -544,7 +530,6 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste
|
|||||||
// switch on type of message and take action
|
// switch on type of message and take action
|
||||||
switch m.Header["Micro-Method"] {
|
switch m.Header["Micro-Method"] {
|
||||||
case "advert":
|
case "advert":
|
||||||
now := time.Now()
|
|
||||||
pbRtrAdvert := &pbRtr.Advert{}
|
pbRtrAdvert := &pbRtr.Advert{}
|
||||||
if err := proto.Unmarshal(m.Body, pbRtrAdvert); err != nil {
|
if err := proto.Unmarshal(m.Body, pbRtrAdvert); err != nil {
|
||||||
log.Debugf("Network fail to unmarshal advert message: %v", err)
|
log.Debugf("Network fail to unmarshal advert message: %v", err)
|
||||||
@ -554,27 +539,13 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste
|
|||||||
if pbRtrAdvert.Id == n.options.Id {
|
if pbRtrAdvert.Id == n.options.Id {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// loookup advertising node in our peers
|
|
||||||
n.Lock()
|
|
||||||
log.Debugf("Network received advert message from: %s", pbRtrAdvert.Id)
|
log.Debugf("Network received advert message from: %s", pbRtrAdvert.Id)
|
||||||
advertNode, ok := n.peers[pbRtrAdvert.Id]
|
// loookup advertising node in our peers
|
||||||
if !ok {
|
advertNode := n.node.GetPeer(pbRtrAdvert.Id)
|
||||||
// advertising node has not been registered as our peer, yet
|
// if we dont recognize the node as our peer we skip processing its adverts
|
||||||
// let's add it to our peers
|
if advertNode == nil {
|
||||||
advertNode = &node{
|
log.Debugf("Network skipping advert message from unknown peer: %s", pbRtrAdvert.Id)
|
||||||
id: pbRtrAdvert.Id,
|
continue
|
||||||
peers: make(map[string]*node),
|
|
||||||
lastSeen: now,
|
|
||||||
}
|
|
||||||
n.peers[pbRtrAdvert.Id] = advertNode
|
|
||||||
n.Unlock()
|
|
||||||
// send a solicit message when discovering a new node
|
|
||||||
msg := &pbRtr.Solicit{
|
|
||||||
Id: n.options.Id,
|
|
||||||
}
|
|
||||||
if err := n.sendMsg("solicit", msg, ControlChannel); err != nil {
|
|
||||||
log.Debugf("Network failed to send solicit message: %s", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var events []*router.Event
|
var events []*router.Event
|
||||||
@ -585,13 +556,17 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste
|
|||||||
// as our peer when we received the advert from it
|
// as our peer when we received the advert from it
|
||||||
if advertNode.address == "" {
|
if advertNode.address == "" {
|
||||||
advertNode.address = event.Route.Gateway
|
advertNode.address = event.Route.Gateway
|
||||||
|
if ok := n.node.UpdatePeer(advertNode); !ok {
|
||||||
|
log.Debugf("Network failed to update peer: %s", advertNode.id)
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if advertising node id is not the same as Route.Router
|
// if advertising node id is not the same as Route.Router
|
||||||
// we know the advertising node is not the origin of the route
|
// we know the advertising node is not the origin of the route
|
||||||
if advertNode.id != event.Route.Router {
|
if advertNode.id != event.Route.Router {
|
||||||
// if the origin router is not the advertising node peer
|
// if the origin router is not the advertising node peer
|
||||||
// we can't rule out potential routing loops so we bail here
|
// we can't rule out potential routing loops so we bail here
|
||||||
if _, ok := advertNode.peers[event.Route.Router]; !ok {
|
if peer := advertNode.GetPeer(event.Route.Router); peer == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -605,7 +580,9 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste
|
|||||||
Metric: int(event.Route.Metric),
|
Metric: int(event.Route.Metric),
|
||||||
}
|
}
|
||||||
// set the route metric
|
// set the route metric
|
||||||
|
n.node.RLock()
|
||||||
n.setRouteMetric(&route)
|
n.setRouteMetric(&route)
|
||||||
|
n.node.RUnlock()
|
||||||
// throw away metric bigger than 1000
|
// throw away metric bigger than 1000
|
||||||
if route.Metric > 1000 {
|
if route.Metric > 1000 {
|
||||||
log.Debugf("Network route metric %d dropping node: %s", route.Metric, route.Router)
|
log.Debugf("Network route metric %d dropping node: %s", route.Metric, route.Router)
|
||||||
@ -619,6 +596,7 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste
|
|||||||
}
|
}
|
||||||
events = append(events, e)
|
events = append(events, e)
|
||||||
}
|
}
|
||||||
|
// create an advert and process it
|
||||||
advert := &router.Advert{
|
advert := &router.Advert{
|
||||||
Id: pbRtrAdvert.Id,
|
Id: pbRtrAdvert.Id,
|
||||||
Type: router.AdvertType(pbRtrAdvert.Type),
|
Type: router.AdvertType(pbRtrAdvert.Type),
|
||||||
@ -627,9 +605,9 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste
|
|||||||
Events: events,
|
Events: events,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("Network router processing advert: %s", advert.Id)
|
||||||
if err := n.Router.Process(advert); err != nil {
|
if err := n.Router.Process(advert); err != nil {
|
||||||
log.Debugf("Network failed to process advert %s: %v", advert.Id, err)
|
log.Debugf("Network failed to process advert %s: %v", advert.Id, err)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
case "solicit":
|
case "solicit":
|
||||||
pbRtrSolicit := &pbRtr.Solicit{}
|
pbRtrSolicit := &pbRtr.Solicit{}
|
||||||
@ -642,6 +620,7 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste
|
|||||||
if pbRtrSolicit.Id == n.options.Id {
|
if pbRtrSolicit.Id == n.options.Id {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
log.Debugf("Network router flushing routes for: %s", pbRtrSolicit.Id)
|
||||||
// advertise all the routes when a new node has connected
|
// advertise all the routes when a new node has connected
|
||||||
if err := n.Router.Solicit(); err != nil {
|
if err := n.Router.Solicit(); err != nil {
|
||||||
log.Debugf("Network failed to solicit routes: %s", err)
|
log.Debugf("Network failed to solicit routes: %s", err)
|
||||||
@ -685,7 +664,7 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A
|
|||||||
Timestamp: advert.Timestamp.UnixNano(),
|
Timestamp: advert.Timestamp.UnixNano(),
|
||||||
Events: events,
|
Events: events,
|
||||||
}
|
}
|
||||||
if err := n.sendMsg("advert", msg, NetworkChannel); err != nil {
|
if err := n.sendMsg("advert", msg, ControlChannel); err != nil {
|
||||||
log.Debugf("Network failed to advertise routes: %v", err)
|
log.Debugf("Network failed to advertise routes: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -823,16 +802,27 @@ func (n *network) close() error {
|
|||||||
// Close closes network connection
|
// Close closes network connection
|
||||||
func (n *network) Close() error {
|
func (n *network) Close() error {
|
||||||
n.Lock()
|
n.Lock()
|
||||||
defer n.Unlock()
|
|
||||||
|
|
||||||
if !n.connected {
|
if !n.connected {
|
||||||
|
n.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-n.closed:
|
case <-n.closed:
|
||||||
|
n.Unlock()
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
|
// TODO: send close message to the network channel
|
||||||
|
close(n.closed)
|
||||||
|
// set connected to false
|
||||||
|
n.connected = false
|
||||||
|
|
||||||
|
// unlock the lock otherwise we'll deadlock sending the close
|
||||||
|
n.Unlock()
|
||||||
|
|
||||||
|
// send close message only if we managed to connect to NetworkChannel
|
||||||
|
log.Debugf("Sending close message from: %s", n.options.Id)
|
||||||
msg := &pbNet.Close{
|
msg := &pbNet.Close{
|
||||||
Node: &pbNet.Node{
|
Node: &pbNet.Node{
|
||||||
Id: n.options.Id,
|
Id: n.options.Id,
|
||||||
@ -842,10 +832,6 @@ func (n *network) Close() error {
|
|||||||
if err := n.sendMsg("close", msg, NetworkChannel); err != nil {
|
if err := n.sendMsg("close", msg, NetworkChannel); err != nil {
|
||||||
log.Debugf("Network failed to send close message: %s", err)
|
log.Debugf("Network failed to send close message: %s", err)
|
||||||
}
|
}
|
||||||
// TODO: send close message to the network channel
|
|
||||||
close(n.closed)
|
|
||||||
// set connected to false
|
|
||||||
n.connected = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return n.close()
|
return n.close()
|
||||||
|
@ -22,11 +22,8 @@ func (n *Network) ListPeers(ctx context.Context, req *pbNet.PeerRequest, resp *p
|
|||||||
depth = network.MaxDepth
|
depth = network.MaxDepth
|
||||||
}
|
}
|
||||||
|
|
||||||
// get node peers
|
|
||||||
nodePeers := n.Network.Peers()
|
|
||||||
|
|
||||||
// get peers encoded into protobuf
|
// get peers encoded into protobuf
|
||||||
peers := network.PeersToProto(n.Network, nodePeers, depth)
|
peers := network.PeersToProto(n.Network, depth)
|
||||||
|
|
||||||
resp.Peers = peers
|
resp.Peers = peers
|
||||||
|
|
||||||
|
134
network/node.go
134
network/node.go
@ -2,7 +2,6 @@ package network
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"errors"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -44,6 +43,86 @@ func (n *node) Network() Network {
|
|||||||
return n.network
|
return n.network
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddPeer adds a new peer to node
|
||||||
|
// It returns false if the peer already exists
|
||||||
|
func (n *node) AddPeer(peer *node) bool {
|
||||||
|
n.Lock()
|
||||||
|
defer n.Unlock()
|
||||||
|
|
||||||
|
if _, ok := n.peers[peer.id]; !ok {
|
||||||
|
n.peers[peer.id] = peer
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeer returns a peer if it exists
|
||||||
|
// It returns nil if the peer was not found
|
||||||
|
func (n *node) GetPeer(id string) *node {
|
||||||
|
n.RLock()
|
||||||
|
defer n.RUnlock()
|
||||||
|
|
||||||
|
p, ok := n.peers[id]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
peer := &node{
|
||||||
|
id: p.id,
|
||||||
|
address: p.address,
|
||||||
|
peers: make(map[string]*node),
|
||||||
|
network: p.network,
|
||||||
|
lastSeen: p.lastSeen,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: recursively retrieve all of its peers
|
||||||
|
for id, pop := range p.peers {
|
||||||
|
peer.peers[id] = pop
|
||||||
|
}
|
||||||
|
|
||||||
|
return peer
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePeer updates a peer if it exists
|
||||||
|
// It returns false if the peer does not exist
|
||||||
|
func (n *node) UpdatePeer(peer *node) bool {
|
||||||
|
n.Lock()
|
||||||
|
defer n.Unlock()
|
||||||
|
|
||||||
|
if _, ok := n.peers[peer.id]; ok {
|
||||||
|
n.peers[peer.id] = peer
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePeer deletes a peer if it exists
|
||||||
|
func (n *node) DeletePeer(id string) {
|
||||||
|
n.Lock()
|
||||||
|
defer n.Unlock()
|
||||||
|
|
||||||
|
delete(n.peers, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh updates node timestamp
|
||||||
|
func (n *node) RefreshPeer(id string, now time.Time) bool {
|
||||||
|
n.Lock()
|
||||||
|
defer n.Unlock()
|
||||||
|
|
||||||
|
peer, ok := n.peers[id]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if peer.lastSeen.Before(now) {
|
||||||
|
peer.lastSeen = now
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Nodes returns a slice if all nodes in node topology
|
// Nodes returns a slice if all nodes in node topology
|
||||||
func (n *node) Nodes() []Node {
|
func (n *node) Nodes() []Node {
|
||||||
// we need to freeze the network graph here
|
// we need to freeze the network graph here
|
||||||
@ -90,10 +169,11 @@ func (n *node) Nodes() []Node {
|
|||||||
func (n *node) topology(depth uint) *node {
|
func (n *node) topology(depth uint) *node {
|
||||||
// make a copy of yourself
|
// make a copy of yourself
|
||||||
node := &node{
|
node := &node{
|
||||||
id: n.id,
|
id: n.id,
|
||||||
address: n.address,
|
address: n.address,
|
||||||
peers: make(map[string]*node),
|
peers: make(map[string]*node),
|
||||||
network: n.network,
|
network: n.network,
|
||||||
|
lastSeen: n.lastSeen,
|
||||||
}
|
}
|
||||||
|
|
||||||
// return if we reach requested depth or we have no more peers
|
// return if we reach requested depth or we have no more peers
|
||||||
@ -128,31 +208,13 @@ func (n *node) Peers() []Node {
|
|||||||
return peers
|
return peers
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateTopology updates node peer topology down to given depth
|
// UnpackPeerTopology unpacks pb.Peer into node topology of given depth
|
||||||
func (n *node) updatePeerTopology(pbPeer *pb.Peer, depth uint) error {
|
func UnpackPeerTopology(pbPeer *pb.Peer, lastSeen time.Time, depth uint) *node {
|
||||||
n.Lock()
|
|
||||||
defer n.Unlock()
|
|
||||||
|
|
||||||
if pbPeer == nil {
|
|
||||||
return errors.New("peer not initialized")
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpack Peer topology into *node
|
|
||||||
peer := unpackPeer(pbPeer, depth)
|
|
||||||
|
|
||||||
// update node peers with new topology
|
|
||||||
n.peers[pbPeer.Node.Id] = peer
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// unpackPeer unpacks pb.Peer into node topology of given depth
|
|
||||||
// NOTE: this function is not thread-safe
|
|
||||||
func unpackPeer(pbPeer *pb.Peer, depth uint) *node {
|
|
||||||
peerNode := &node{
|
peerNode := &node{
|
||||||
id: pbPeer.Node.Id,
|
id: pbPeer.Node.Id,
|
||||||
address: pbPeer.Node.Address,
|
address: pbPeer.Node.Address,
|
||||||
peers: make(map[string]*node),
|
peers: make(map[string]*node),
|
||||||
|
lastSeen: lastSeen,
|
||||||
}
|
}
|
||||||
|
|
||||||
// return if have either reached the depth or have no more peers
|
// return if have either reached the depth or have no more peers
|
||||||
@ -165,7 +227,7 @@ func unpackPeer(pbPeer *pb.Peer, depth uint) *node {
|
|||||||
|
|
||||||
peers := make(map[string]*node)
|
peers := make(map[string]*node)
|
||||||
for _, pbPeer := range pbPeer.Peers {
|
for _, pbPeer := range pbPeer.Peers {
|
||||||
peer := unpackPeer(pbPeer, depth)
|
peer := UnpackPeerTopology(pbPeer, lastSeen, depth)
|
||||||
peers[pbPeer.Node.Id] = peer
|
peers[pbPeer.Node.Id] = peer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,19 +265,19 @@ func peerTopology(peer Node, depth uint) *pb.Peer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PeersToProto returns node peers graph encoded into protobuf
|
// PeersToProto returns node peers graph encoded into protobuf
|
||||||
func PeersToProto(root Node, peers []Node, depth uint) *pb.Peer {
|
func PeersToProto(node Node, depth uint) *pb.Peer {
|
||||||
// network node aka root node
|
// network node aka root node
|
||||||
node := &pb.Node{
|
pbNode := &pb.Node{
|
||||||
Id: root.Id(),
|
Id: node.Id(),
|
||||||
Address: root.Address(),
|
Address: node.Address(),
|
||||||
}
|
}
|
||||||
// we will build proto topology into this
|
// we will build proto topology into this
|
||||||
pbPeers := &pb.Peer{
|
pbPeers := &pb.Peer{
|
||||||
Node: node,
|
Node: pbNode,
|
||||||
Peers: make([]*pb.Peer, 0),
|
Peers: make([]*pb.Peer, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, peer := range peers {
|
for _, peer := range node.Peers() {
|
||||||
pbPeer := peerTopology(peer, depth)
|
pbPeer := peerTopology(peer, depth)
|
||||||
pbPeers.Peers = append(pbPeers.Peers, pbPeer)
|
pbPeers.Peers = append(pbPeers.Peers, pbPeer)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package network
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
pb "github.com/micro/go-micro/network/proto"
|
pb "github.com/micro/go-micro/network/proto"
|
||||||
)
|
)
|
||||||
@ -185,20 +186,7 @@ func TestPeers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdatePeerTopology(t *testing.T) {
|
func TestUnpackPeerTopology(t *testing.T) {
|
||||||
// single node
|
|
||||||
single := &node{
|
|
||||||
id: testNodeId,
|
|
||||||
address: testNodeAddress,
|
|
||||||
peers: make(map[string]*node),
|
|
||||||
network: newNetwork(Name(testNodeNetName)),
|
|
||||||
}
|
|
||||||
// nil peer should return error
|
|
||||||
if err := single.updatePeerTopology(nil, 5); err == nil {
|
|
||||||
t.Errorf("Expected error, got %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// update with peer that is not yet in the peer map
|
|
||||||
pbPeer := &pb.Peer{
|
pbPeer := &pb.Peer{
|
||||||
Node: &pb.Node{
|
Node: &pb.Node{
|
||||||
Id: "newPeer",
|
Id: "newPeer",
|
||||||
@ -207,14 +195,11 @@ func TestUpdatePeerTopology(t *testing.T) {
|
|||||||
Peers: make([]*pb.Peer, 0),
|
Peers: make([]*pb.Peer, 0),
|
||||||
}
|
}
|
||||||
// it should add pbPeer to the single node peers
|
// it should add pbPeer to the single node peers
|
||||||
if err := single.updatePeerTopology(pbPeer, 5); err != nil {
|
peer := UnpackPeerTopology(pbPeer, time.Now(), 5)
|
||||||
t.Errorf("Error updating topology: %s", err)
|
if peer.id != pbPeer.Node.Id {
|
||||||
}
|
t.Errorf("Expected peer id %s, found: %s", pbPeer.Node.Id, peer.id)
|
||||||
if _, ok := single.peers[pbPeer.Node.Id]; !ok {
|
|
||||||
t.Errorf("Expected %s to be added to %s peers", pbPeer.Node.Id, single.id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// complicated node graph
|
|
||||||
node := testSetup()
|
node := testSetup()
|
||||||
// build a simple topology to update node peer1
|
// build a simple topology to update node peer1
|
||||||
peer1 := node.peers["peer1"]
|
peer1 := node.peers["peer1"]
|
||||||
@ -243,14 +228,12 @@ func TestUpdatePeerTopology(t *testing.T) {
|
|||||||
Node: pbPeer1Node,
|
Node: pbPeer1Node,
|
||||||
Peers: []*pb.Peer{pbPeer111, pbPeer121},
|
Peers: []*pb.Peer{pbPeer111, pbPeer121},
|
||||||
}
|
}
|
||||||
// update peer1 topology
|
// unpack peer1 topology
|
||||||
if err := node.updatePeerTopology(pbPeer1, 5); err != nil {
|
peer = UnpackPeerTopology(pbPeer1, time.Now(), 5)
|
||||||
t.Errorf("Error updating topology: %s", err)
|
|
||||||
}
|
|
||||||
// make sure peer1 topology has been correctly updated
|
// make sure peer1 topology has been correctly updated
|
||||||
newPeerIds := []string{pbPeer111.Node.Id, pbPeer121.Node.Id}
|
newPeerIds := []string{pbPeer111.Node.Id, pbPeer121.Node.Id}
|
||||||
for _, id := range newPeerIds {
|
for _, id := range newPeerIds {
|
||||||
if _, ok := node.peers["peer1"].peers[id]; !ok {
|
if _, ok := peer.peers[id]; !ok {
|
||||||
t.Errorf("Expected %s to be a peer of %s", id, "peer1")
|
t.Errorf("Expected %s to be a peer of %s", id, "peer1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -266,7 +249,7 @@ func TestPeersToProto(t *testing.T) {
|
|||||||
}
|
}
|
||||||
topCount := 0
|
topCount := 0
|
||||||
|
|
||||||
protoPeers := PeersToProto(single, single.Peers(), 0)
|
protoPeers := PeersToProto(single, 0)
|
||||||
|
|
||||||
if len(protoPeers.Peers) != topCount {
|
if len(protoPeers.Peers) != topCount {
|
||||||
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
|
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
|
||||||
@ -282,7 +265,7 @@ func TestPeersToProto(t *testing.T) {
|
|||||||
peerIds[id] = true
|
peerIds[id] = true
|
||||||
}
|
}
|
||||||
// depth 1 should give us immmediate neighbours only
|
// depth 1 should give us immmediate neighbours only
|
||||||
protoPeers = PeersToProto(node, node.Peers(), 1)
|
protoPeers = PeersToProto(node, 1)
|
||||||
|
|
||||||
if len(protoPeers.Peers) != topCount {
|
if len(protoPeers.Peers) != topCount {
|
||||||
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
|
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
|
||||||
|
Loading…
Reference in New Issue
Block a user