Browse Source

Make root and relay selection somewhat more robust.

Adam Ierymenko 9 years ago
parent
commit
4e9d430476
3 changed files with 44 additions and 26 deletions
  1. 22 4
      node/Peer.hpp
  2. 5 2
      node/Switch.cpp
  3. 17 20
      node/Topology.cpp

+ 22 - 4
node/Peer.hpp

@@ -236,15 +236,33 @@ public:
 	inline uint64_t activelyTransferringFrames(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
 
 	/**
-	 * @return Current latency or 0 if unknown (max: 65535)
+	 * @return Latency in milliseconds or 0 if unknown
 	 */
-	inline unsigned int latency() const
-		throw()
+	inline unsigned int latency() const { return _latency; }
+
+	/**
+	 * This computes a quality score for relays and root servers
+	 *
+	 * If we haven't heard anything from these in ZT_PEER_ACTIVITY_TIMEOUT, they
+	 * receive the worst possible quality (max unsigned int). Otherwise the
+	 * quality is a product of latency and the number of potential missed
+	 * pings. This causes roots and relays to switch over a bit faster if they
+	 * fail.
+	 *
+	 * @return Relay quality score computed from latency and other factors, lower is better
+	 */
+	inline unsigned int relayQuality(const uint64_t now) const
 	{
+		const uint64_t tsr = now - _lastReceive;
+		if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT)
+			return (~(unsigned int)0);
 		unsigned int l = _latency;
-		return std::min(l,(unsigned int)65535);
+		if (!l)
+			l = 0xffff;
+		return (l * (((unsigned int)tsr / (ZT_PEER_DIRECT_PING_DELAY + 1000)) + 1));
 	}
 
+
 	/**
 	 * Update latency with a new direct measurment
 	 *

+ 5 - 2
node/Switch.cpp

@@ -741,12 +741,15 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
 		if (!viaPath) {
 			// See if this network has a preferred relay (if packet has an associated network)
 			if (nconf) {
-				unsigned int latency = ~((unsigned int)0);
+				unsigned int bestq = ~((unsigned int)0);
 				for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) {
 					if (r->first != peer->address()) {
 						SharedPtr<Peer> rp(RR->topology->getPeer(r->first));
-						if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency))
+						const unsigned int q = rp->relayQuality(now);
+						if ((rp)&&(q < bestq)) { // SUBTILE: < == don't use these if they are nil quality (unsigned int max), instead use a root
+							bestq = q;
 							rp.swap(relay);
+						}
 					}
 				}
 			}

+ 17 - 20
node/Topology.cpp

@@ -227,33 +227,30 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
 
 	} else {
 		/* If I am not a root server, the best root server is the active one with
-		 * the lowest latency. */
+		 * the lowest quality score. (lower == better) */
 
-		unsigned int bestLatencyOverall = ~((unsigned int)0);
-		unsigned int bestLatencyNotAvoid = ~((unsigned int)0);
+		unsigned int bestQualityOverall = ~((unsigned int)0);
+		unsigned int bestQualityNotAvoid = ~((unsigned int)0);
 		const SharedPtr<Peer> *bestOverall = (const SharedPtr<Peer> *)0;
 		const SharedPtr<Peer> *bestNotAvoid = (const SharedPtr<Peer> *)0;
 
 		for(std::vector< SharedPtr<Peer> >::const_iterator r(_rootPeers.begin());r!=_rootPeers.end();++r) {
-			if ((*r)->hasActiveDirectPath(now)) {
-				bool avoiding = false;
-				for(unsigned int i=0;i<avoidCount;++i) {
-					if (avoid[i] == (*r)->address()) {
-						avoiding = true;
-						break;
-					}
-				}
-				unsigned int l = (*r)->latency();
-				if (!l) l = ~l; // zero latency indicates no measurment, so make this 'max'
-				if (l <= bestLatencyOverall) {
-					bestLatencyOverall = l;
-					bestOverall = &(*r);
-				}
-				if ((!avoiding)&&(l <= bestLatencyNotAvoid)) {
-					bestLatencyNotAvoid = l;
-					bestNotAvoid = &(*r);
+			bool avoiding = false;
+			for(unsigned int i=0;i<avoidCount;++i) {
+				if (avoid[i] == (*r)->address()) {
+					avoiding = true;
+					break;
 				}
 			}
+			const unsigned int q = (*r)->relayQuality(now);
+			if (q <= bestQualityOverall) {
+				bestQualityOverall = q;
+				bestOverall = &(*r);
+			}
+			if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
+				bestQualityNotAvoid = q;
+				bestNotAvoid = &(*r);
+			}
 		}
 
 		if (bestNotAvoid) {