/** * bad route, so let's remove everything we have so we can start from scratch. We are maintaining * a bad route up to DEFAULT_ROUTE experition after that we consider it to be ok to retry the same * route We are removing both the route and peer advertisement to force a new route query * * @param src peer that reply with the NACK route info * @param dest original route information */ private void processBadRoute(PeerID badHop, RouteAdvertisement dest) { EndpointAddress addr = router.pid2addr(dest.getDestPeerID()); if (addr == null) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("remove bad route has a bad route info - discard"); } return; } if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("remove bad route info for dest " + dest.display()); if (badHop != null) { LOG.debug("remove bad route bad hop " + badHop); } } try { // check first that we still have the same route, we may already // using a new route RouteAdvertisement currentRoute = router.getRoute(addr, false); if (currentRoute == null) { // we already cleanup the route info return; } // check if we still have the old bad route, we may have // already updated the route if (!currentRoute.equals(dest)) { // check if the bad hop is not the destination // if it is then we still have a bad route if (!(badHop.equals(router.addr2pid(addr)))) { // check if the new route may still contain the bad hop // the known bad hop is the hop after the src peer that // responded with a NACK route // In this case we also consider the route bad if (badHop != null) { // this should really not be null if (!currentRoute.containsHop(badHop)) { return; // we are ok } else { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("current route is bad because it contains known bad hop" + badHop); } } } else { // we could get the bad hop, so consider the route ok return; } } else { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("current route is bad because it contains known bad destination" + badHop); } } } // keep the bad one in a cache table so we don't retry them // right away. We use the default route timeout BadRoute badRoute = ((BadRoute) router.getBadRoute(addr)); if (badRoute != null) { Long nextTry = badRoute.getExpiration(); if (nextTry.longValue() > System.currentTimeMillis()) { // nothing to do the information is still valid } else { // It is ancient knowlege update it nextTry = new Long(TimeUtils.toAbsoluteTimeMillis(BADROUTE_EXPIRATION)); badRoute.setExpiration(nextTry); } List badHops = badRoute.getHops(); // check if we have to add a new bad hop // to our bad route if (badHop != null) { if (!badHops.contains(badHop)) { badHops.add(badHop); // new expiration nextTry = new Long(TimeUtils.toAbsoluteTimeMillis(BADROUTE_EXPIRATION)); badRoute.setHops(badHops); badRoute.setExpiration(nextTry); } } router.setBadRoute(addr, badRoute); return; } else { // create a new NACK route entry Vector badHops = new Vector(); if (badHop != null) { badHops.add(badHop); } badRoute = new BadRoute( dest, new Long(TimeUtils.toAbsoluteTimeMillis(BADROUTE_EXPIRATION)), badHops); router.setBadRoute(addr, badRoute); } // remove route from route CM routeCM.flushRoute(addr); // let's remove the remote route info from the routing table // we do this after we removed the entries from the CM // to avoid that another thread is putting back the entry router.removeRoute(addr); } catch (Exception ex) { if (LOG.isEnabledFor(Level.WARN)) { LOG.warn("exception during bad route removal", ex); } } }
/** * issue a new route discovery resolver request * * @param peer the destination as a logical enpoint address */ protected void findRoute(EndpointAddress peer) { RouteAdvertisement myRoute = router.getMyLocalRoute(); // No need to pursue further if we haven't // initialize our own route as responding // peers are not going to be able to respond to us. if (myRoute == null) { if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("Cannot issue a find route if we don't know our own route"); } return; } if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("Find route for peer = " + peer); } try { // create a new RouteQuery message RouteQuery doc = null; // check if we have some bad route information // for that peer, in that case pass the bad hop count BadRoute badRoute; badRoute = (BadRoute) router.getBadRoute(peer); if (badRoute != null) { // ok we have a bad route // pass the bad hops info as part of the query if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("findRoute sends query: known bad Hops" + badRoute.display()); } doc = new RouteQuery(router.addr2pid(peer), myRoute, badRoute.getHops()); } else { doc = new RouteQuery(router.addr2pid(peer), myRoute, null); } if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("Sending query for peer : " + peer); } ResolverQuery query = new ResolverQuery( routerSName, credentialDoc, localPeerId.toString(), doc.toString(), qid++); // only run SRDI if we are a rendezvous if (group.isRendezvous()) { // check where to send the query via SRDI Vector results = null; if (srdiIndex != null) { // try to find a least 10 entries, will pick up one // randomly. This will protect against retry. It is // likely that a number of RDV will know about a route results = srdiIndex.query("route", "DstPID", router.addr2pid(peer).toString(), 10); if (results != null && results.size() > 0) { // use SRDI to send the query // remove any non rdv peers from the candidate list // and garbage collect the index in the process Vector clean = cleanupAnyEdges(query.getSrc(), results); if (clean.size() > 0) { // The purpose of incrementing the hopcount // when an SRDI index match is found (we got a // pointer to a rdv that should have the route) is to // restrict any further forwarding. The increment // count is only done when a matching SRDI index is // found. Not when the replica is selected as we // still need to forward the query. This restriction // is purposelly done to avoid too many longjumps // within a walk. query.incrementHopCount(); srdi.forwardQuery(clean, query, 1); if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("found an srdi entry forwarding query to SRDI peer"); } return; } } else { // it is not in our cache, look for the replica peer // we need to send the query PeerID destPeer = srdi.getReplicaPeer(router.addr2pid(peer).toString()); if (destPeer != null && !destPeer.equals(localPeerId)) { // don't push anywhere if we do not have a replica // or we are trying to push to ourself if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("processQuery srdiIndex DHT forward :" + destPeer); } srdi.forwardQuery(destPeer.toString(), query); return; } } } } // if we reach that point then we just use the resolver walk resolver = group.getResolverService(); if (resolver != null) { resolver.sendQuery(null, query); if (LOG.isEnabledFor(Level.DEBUG)) { LOG.debug("find route query sent"); } } else { if (LOG.isEnabledFor(Level.WARN)) { LOG.warn("cannot get the resolver service"); } } } catch (Exception ee) { if (LOG.isEnabledFor(Level.WARN)) { LOG.warn("Exception in findRoute", ee); } } }