From 78d5080d788a53fc610e44785da7705dfc65c602 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Sat, 31 Jan 2009 15:36:24 +0000
Subject: [PATCH]     * Tunnel Pool:       - Remove tunnel from participating
 if can't contact next hop       - Fail outbound build faster if can't contact
 first hop

---
 .../i2p/router/tunnel/pool/BuildExecutor.java |  1 +
 .../i2p/router/tunnel/pool/BuildHandler.java  | 50 +++++++++++++++----
 .../router/tunnel/pool/BuildRequestor.java    | 31 ++++++++++++
 3 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java
index 390fe888dc..3a84f48105 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildExecutor.java
@@ -46,6 +46,7 @@ class BuildExecutor implements Runnable {
         _context.statManager().createRateStat("tunnel.buildRequestTime", "How long it takes to build a tunnel request", "Tunnels", new long[] { 60*1000, 10*60*1000 });
         _context.statManager().createRateStat("tunnel.buildRequestZeroHopTime", "How long it takes to build a zero hop tunnel", "Tunnels", new long[] { 60*1000, 10*60*1000 });
         _context.statManager().createRateStat("tunnel.pendingRemaining", "How many inbound requests are pending after a pass (period is how long the pass takes)?", "Tunnels", new long[] { 60*1000, 10*60*1000 });
+        _context.statManager().createRateStat("tunnel.buildFailFirstHop", "How often we fail to build a OB tunnel because we can't contact the first hop", "Tunnels", new long[] { 60*1000, 10*60*1000 });
 
         // Get stat manager, get recognized bandwidth tiers
         StatManager statMgr = _context.statManager();
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
index 3c2a9dd204..f6b4d34786 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
@@ -61,6 +61,7 @@ class BuildHandler {
 
         _context.statManager().createRateStat("tunnel.decryptRequestTime", "How long it takes to decrypt a new tunnel build request", "Tunnels", new long[] { 60*1000, 10*60*1000 });
         _context.statManager().createRateStat("tunnel.rejectTimeout", "How often we reject a tunnel because we can't find the next hop", "Tunnels", new long[] { 60*1000, 10*60*1000 });
+        _context.statManager().createRateStat("tunnel.rejectTimeout2", "How often we fail a tunnel because we can't contact the next hop", "Tunnels", new long[] { 60*1000, 10*60*1000 });
 
         _context.statManager().createRateStat("tunnel.rejectOverloaded", "How long we had to wait before processing the request (when it was rejected)", "Tunnels", new long[] { 60*1000, 10*60*1000 });
         _context.statManager().createRateStat("tunnel.acceptLoad", "Delay before processing the accepted request", "Tunnels", new long[] { 60*1000, 10*60*1000 });
@@ -413,7 +414,7 @@ class BuildHandler {
         }
     }
 
-    private class TimeoutReq extends JobImpl {
+    private static class TimeoutReq extends JobImpl {
         private BuildMessageState _state;
         private BuildRequestRecord _req;
         private Hash _nextPeer;
@@ -425,10 +426,12 @@ class BuildHandler {
         }
         public String getName() { return "Timeout looking for next peer for tunnel join"; }
         public void runJob() {
-            getContext().statManager().addRateData("tunnel.rejectTimeout", 1, 1);
-            if (_log.shouldLog(Log.WARN))
-                _log.warn("Request " + _state.msg.getUniqueId() 
-                          + " could no be satisfied, as the next peer could not be found: " + _nextPeer.toBase64());
+            getContext().statManager().addRateData("tunnel.rejectTimeout", 1, 0);
+            // logging commented out so class can be static
+            //if (_log.shouldLog(Log.WARN))
+            //    _log.warn("Request " + _state.msg.getUniqueId() 
+            //              + " could no be satisfied, as the next peer could not be found: " + _nextPeer.toBase64());
+
             // ???  should we blame the peer here?   getContext().profileManager().tunnelTimedOut(_nextPeer);
             getContext().messageHistory().tunnelRejected(_state.fromHash, new TunnelId(_req.readReceiveTunnelId()), _nextPeer, 
                                                          "rejected because we couldn't find " + _nextPeer.toBase64() + ": " +
@@ -516,8 +519,9 @@ class BuildHandler {
                        + " from " + (state.fromHash != null ? state.fromHash.toBase64() : 
                                      state.from != null ? state.from.calculateHash().toBase64() : "tunnel"));
 
+        HopConfig cfg = null;
         if (response == 0) {
-            HopConfig cfg = new HopConfig();
+            cfg = new HopConfig();
             cfg.setCreation(_context.clock().now());
             cfg.setExpiration(_context.clock().now() + 10*60*1000);
             cfg.setIVKey(req.readIVKey());
@@ -593,6 +597,8 @@ class BuildHandler {
             msg.setExpiration(state.msg.getMessageExpiration());
             msg.setPriority(300);
             msg.setTarget(nextPeerInfo);
+            if (response == 0)
+                msg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg));
             _context.outNetMessagePool().add(msg);
         } else {
             // send it to the reply tunnel on the reply peer within a new TunnelBuildReplyMessage
@@ -619,6 +625,8 @@ class BuildHandler {
                 outMsg.setMessage(m);
                 outMsg.setPriority(300);
                 outMsg.setTarget(nextPeerInfo);
+                if (response == 0)
+                    outMsg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg));
                 _context.outNetMessagePool().add(outMsg);
             }
         }
@@ -762,7 +770,7 @@ class BuildHandler {
     }
     
     /** normal inbound requests from other people */
-    private class BuildMessageState {
+    private static class BuildMessageState {
         TunnelBuildMessage msg;
         RouterIdentity from;
         Hash fromHash;
@@ -775,7 +783,7 @@ class BuildHandler {
         }
     }
     /** replies for outbound tunnels that we have created */
-    private class BuildReplyMessageState {
+    private static class BuildReplyMessageState {
         TunnelBuildReplyMessage msg;
         long recvTime;
         public BuildReplyMessageState(I2NPMessage m) {
@@ -784,7 +792,7 @@ class BuildHandler {
         }
     }
     /** replies for inbound tunnels we have created */
-    private class BuildEndMessageState {
+    private static class BuildEndMessageState {
         TunnelBuildMessage msg;
         PooledTunnelCreatorConfig cfg;
         long recvTime;
@@ -796,15 +804,35 @@ class BuildHandler {
     }
 
     // noop
-    private class TunnelBuildMessageHandlerJob extends JobImpl {
+    private static class TunnelBuildMessageHandlerJob extends JobImpl {
         private TunnelBuildMessageHandlerJob(RouterContext ctx) { super(ctx); }
         public void runJob() {}
         public String getName() { return "Receive tunnel build message"; }
     }
     // noop
-    private class TunnelBuildReplyMessageHandlerJob extends JobImpl {
+    private static class TunnelBuildReplyMessageHandlerJob extends JobImpl {
         private TunnelBuildReplyMessageHandlerJob(RouterContext ctx) { super(ctx); }
         public void runJob() {}
         public String getName() { return "Receive tunnel build reply message"; }
     }
+
+    /**
+     *  Remove the participating tunnel if we can't contact the next hop
+     *  Not strictly necessary, as the entry doesn't use that much space,
+     *  but it affects capacity calculations
+     */
+    private static class TunnelBuildNextHopFailJob extends JobImpl {
+        HopConfig _cfg;
+        private TunnelBuildNextHopFailJob(RouterContext ctx, HopConfig cfg) {
+            super(ctx);
+            _cfg = cfg;
+        }
+        public String getName() { return "Timeout contacting next peer for tunnel join"; }
+        public void runJob() {
+            getContext().tunnelDispatcher().remove(_cfg);
+            getContext().statManager().addRateData("tunnel.rejectTimeout2", 1, 0);
+            // static, no _log
+            //_log.error("Cant contact next hop for " + _cfg);
+        }
+    }
 }
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java
index a4917772fa..21325be851 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildRequestor.java
@@ -12,6 +12,7 @@ import net.i2p.data.RouterInfo;
 import net.i2p.data.TunnelId;
 import net.i2p.data.i2np.I2NPMessage;
 import net.i2p.data.i2np.TunnelBuildMessage;
+import net.i2p.router.JobImpl;
 import net.i2p.router.OutNetMessage;
 import net.i2p.router.RouterContext;
 import net.i2p.router.TunnelInfo;
@@ -136,6 +137,7 @@ class BuildRequestor {
                 return;
             }
             outMsg.setTarget(peer);
+            outMsg.setOnFailedSendJob(new TunnelBuildFirstHopFailJob(ctx, pool, cfg, exec));
             ctx.outNetMessagePool().add(outMsg);
         }
         if (log.shouldLog(Log.DEBUG))
@@ -213,4 +215,33 @@ class BuildRequestor {
         ctx.jobQueue().addJob(expireJob);
         // can it get much easier?
     }
+
+    /**
+     *  Do two important things if we can't get the build msg to the
+     *  first hop on an outbound tunnel -
+     *  - Call buildComplete() so we can get started on the next build
+     *    without waiting for the full expire time
+     *  - Blame the first hop in the profile
+     *  Most likely to happen on an exploratory tunnel, obviously.
+     *  Can't do this for inbound tunnels since the msg goes out an expl. tunnel.
+     */
+    private static class TunnelBuildFirstHopFailJob extends JobImpl {
+        TunnelPool _pool;
+        PooledTunnelCreatorConfig _cfg;
+        BuildExecutor _exec;
+        private TunnelBuildFirstHopFailJob(RouterContext ctx, TunnelPool pool, PooledTunnelCreatorConfig cfg, BuildExecutor exec) {
+            super(ctx);
+            _cfg = cfg;
+            _exec = exec;
+            _pool = pool;
+        }
+        public String getName() { return "Timeout contacting first peer for OB tunnel"; }
+        public void runJob() {
+            _exec.buildComplete(_cfg, _pool);
+            getContext().profileManager().tunnelTimedOut(_cfg.getPeer(1));
+            getContext().statManager().addRateData("tunnel.buildFailFirstHop", 1, 0);
+            // static, no _log
+            //System.err.println("Cant contact first hop for " + _cfg);
+        }
+    }
 }
-- 
GitLab