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 390fe888dcc2ca829b974db6543edb9d1db55bdc..3a84f48105159cd1905df7334d2492c64c89946a 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 3c2a9dd204af3018fedf8bb54df98434f393e2bf..f6b4d3478682a3c24bdc9cb2d50408eb4a772ae0 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 a4917772fa5adfeba5a055b6620036d52aad8fb3..21325be85189c68045f0405a60f127decb6db6ec 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); + } + } }