From af99250a69296cf2699f999f6fff67c9afd77f44 Mon Sep 17 00:00:00 2001 From: zzz <zzz@i2pmail.org> Date: Wed, 8 Feb 2023 09:20:10 -0500 Subject: [PATCH] NetDB: Further restrict what RIs we lookup before dropping if floodfill, unreachable, or L class Only kicks in if we don't have a lot of RIs to begin with --- .../FloodfillNetworkDatabaseFacade.java | 83 ++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java index 3560ee785a..40535958cb 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java @@ -16,11 +16,15 @@ import net.i2p.data.i2np.DatabaseLookupMessage; import net.i2p.data.i2np.DatabaseStoreMessage; import net.i2p.data.router.RouterInfo; import net.i2p.data.router.RouterKeyGenerator; +import net.i2p.router.peermanager.DBHistory; +import net.i2p.router.peermanager.PeerProfile; import net.i2p.router.Job; import net.i2p.router.JobImpl; import net.i2p.router.OutNetMessage; import net.i2p.router.Router; import net.i2p.router.RouterContext; +import net.i2p.stat.Rate; +import net.i2p.stat.RateStat; import net.i2p.util.ConcurrentHashSet; import net.i2p.util.Log; import net.i2p.util.SystemVersion; @@ -402,7 +406,7 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad public static boolean isFloodfill(RouterInfo peer) { if (peer == null) return false; String caps = peer.getCapabilities(); - return caps.indexOf(FloodfillNetworkDatabaseFacade.CAPABILITY_FLOODFILL) >= 0; + return caps.indexOf(CAPABILITY_FLOODFILL) >= 0; } public List<RouterInfo> getKnownRouterData() { @@ -609,10 +613,11 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad // drop the peer in these cases // yikes don't do this - stack overflow // getFloodfillPeers().size() == 0 || // yikes2 don't do this either - deadlock! // getKnownRouters() < MIN_REMAINING_ROUTERS || + long uptime = _context.router().getUptime(); int knownRouters = getKBucketSetSize(); if (info.getNetworkId() == _networkID && (knownRouters < MIN_REMAINING_ROUTERS || - _context.router().getUptime() < DONT_FAIL_PERIOD || + uptime < DONT_FAIL_PERIOD || _context.commSystem().countActivePeers() <= MIN_ACTIVE_PEERS)) { if (_log.shouldInfo()) _log.info("Not failing " + peer.toBase64() + " as we are just starting up or have problems"); @@ -631,6 +636,80 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad super.lookupBeforeDropping(peer, info); return; } + + // The following doesn't kick in most of the time, because of + // the MAX_DB_BEFORE_SKIPPING_SEARCH check above, + // that value is pretty small compared to typical netdb sizes. + + String caps = info.getCapabilities(); + if (caps.indexOf(Router.CAPABILITY_UNREACHABLE) >= 0 || + caps.indexOf(Router.CAPABILITY_BW32) >= 0) { + super.lookupBeforeDropping(peer, info); + return; + } + if (caps.indexOf(CAPABILITY_FLOODFILL) >= 0) { + PeerProfile prof = _context.profileOrganizer().getProfile(peer); + if (prof == null) { + //_log.warn("skip lookup no profile " + peer.toBase64()); + super.lookupBeforeDropping(peer, info); + return; + } + // classification similar to that in FloodfillPeerSelector + long now = _context.clock().now(); + long installed = _context.getProperty("router.firstInstalled", 0L); + boolean enforceHeard = installed > 0 && (now - installed) > 2*60*60*1000; + if (enforceHeard && prof.getFirstHeardAbout() > now - 3*60*60*1000) { + //_log.warn("skip lookup new " + DataHelper.formatTime(prof.getFirstHeardAbout()) + ' ' + peer.toBase64()); + super.lookupBeforeDropping(peer, info); + return; + } + DBHistory hist = prof.getDBHistory(); + if (hist == null) { + //_log.warn("skip lookup no dbhist " + peer.toBase64()); + super.lookupBeforeDropping(peer, info); + return; + } + long cutoff = now - 30*60*1000; + long lastLookupSuccess = hist.getLastLookupSuccessful(); + long lastStoreSuccess = hist.getLastStoreSuccessful(); + if (uptime > 30*60*1000 && + lastLookupSuccess < cutoff && + lastStoreSuccess < cutoff) { + //_log.warn("skip lookup no db success " + peer.toBase64()); + super.lookupBeforeDropping(peer, info); + return; + } + cutoff = now - 2*60*60*1000; + long lastLookupFailed = hist.getLastLookupFailed(); + long lastStoreFailed = hist.getLastStoreFailed(); + if (lastLookupFailed > cutoff || + lastStoreFailed > cutoff || + lastLookupFailed > lastLookupSuccess || + lastStoreFailed > lastStoreSuccess) { + //_log.warn("skip lookup dbhist store fail " + DataHelper.formatTime(lastStoreFailed) + " lookup fail " + DataHelper.formatTime(lastLookupFailed) + ' ' + peer.toBase64()); + super.lookupBeforeDropping(peer, info); + return; + } + double maxFailRate = 0.95; + if (_context.router().getUptime() > 60*60*1000) { + RateStat rs = _context.statManager().getRate("peer.failedLookupRate"); + if (rs != null) { + Rate r = rs.getRate(60*60*1000); + if (r != null) { + double currentFailRate = r.getAverageValue(); + maxFailRate = Math.min(0.95d, Math.max(0.20d, 1.25d * currentFailRate)); + } + } + } + double failRate = hist.getFailedLookupRate().getRate(60*60*1000).getAverageValue(); + if (failRate >= 1 || failRate > maxFailRate) { + //_log.warn("skip lookup fail rate " + failRate + " max " + maxFailRate + ' ' + peer.toBase64()); + super.lookupBeforeDropping(peer, info); + return; + } + } + + // this sends out the search to the floodfill peers even if we already have the // entry locally, firing no job if it gets a reply with an updated value (meaning // we shouldn't drop them but instead use the new data), or if they all time out, -- GitLab