diff --git a/router/java/src/net/i2p/router/TunnelInfo.java b/router/java/src/net/i2p/router/TunnelInfo.java index a3b89219382000a7a617b9b7d274b15e91276278..0f038e5d2df25cd6922f3387a813312488154458 100644 --- a/router/java/src/net/i2p/router/TunnelInfo.java +++ b/router/java/src/net/i2p/router/TunnelInfo.java @@ -55,6 +55,7 @@ public class TunnelInfo extends DataStructureImpl { private boolean _ready; private boolean _wasEverReady; private int _messagesProcessed; + private int _tunnelFailures; public TunnelInfo(I2PAppContext context) { _context = context; @@ -77,6 +78,7 @@ public class TunnelInfo extends DataStructureImpl { _created = _context.clock().now(); _lastTested = -1; _messagesProcessed = 0; + _tunnelFailures = 0; } public TunnelId getTunnelId() { return _id; } @@ -182,6 +184,13 @@ public class TunnelInfo extends DataStructureImpl { /** we have just processed a message for this tunnel */ public void messageProcessed() { _messagesProcessed++; } + /** + * the tunnel was (potentially) unable to pass a message through. + * + * @return the new number of tunnel failures ever for this tunnel + */ + public int incrementFailures() { return ++_tunnelFailures; } + public void readBytes(InputStream in) throws DataFormatException, IOException { _options = DataHelper.readProperties(in); Boolean includeDest = DataHelper.readBoolean(in); diff --git a/router/java/src/net/i2p/router/tunnelmanager/ClientTunnelPool.java b/router/java/src/net/i2p/router/tunnelmanager/ClientTunnelPool.java index e03a1dc64dcd7cd346aa3d2abc552506aec0524e..784b19db6e3979c2e5e9df18df4f0ad64f6eecb4 100644 --- a/router/java/src/net/i2p/router/tunnelmanager/ClientTunnelPool.java +++ b/router/java/src/net/i2p/router/tunnelmanager/ClientTunnelPool.java @@ -131,7 +131,7 @@ class ClientTunnelPool { * */ public int getSafePoolSize() { - return getSafePoolSize(0); + return getSafePoolSize(2*60*1000); } /** * Get the safe # pools at some point in the future @@ -140,7 +140,7 @@ class ClientTunnelPool { */ public int getSafePoolSize(long futureMs) { int numSafe = 0; - long expireAfter = _context.clock().now() + Router.CLOCK_FUDGE_FACTOR + futureMs; + long expireAfter = _context.clock().now() + futureMs; for (Iterator iter = getInboundTunnelIds().iterator(); iter.hasNext(); ) { TunnelId id = (TunnelId)iter.next(); TunnelInfo info = getInboundTunnel(id); diff --git a/router/java/src/net/i2p/router/tunnelmanager/ClientTunnelPoolManagerJob.java b/router/java/src/net/i2p/router/tunnelmanager/ClientTunnelPoolManagerJob.java index a2fe863746ef98b78fcfc9b16fa8ce0f7bc1806c..965ca4348fd8292ab3fbbaeafd8bde2caae04207 100644 --- a/router/java/src/net/i2p/router/tunnelmanager/ClientTunnelPoolManagerJob.java +++ b/router/java/src/net/i2p/router/tunnelmanager/ClientTunnelPoolManagerJob.java @@ -50,7 +50,7 @@ class ClientTunnelPoolManagerJob extends JobImpl { return; } int requestedPoolSize = _clientPool.getClientSettings().getNumInboundTunnels(); - int safePoolSize = _clientPool.getSafePoolSize(POOL_CHECK_DELAY); + int safePoolSize = _clientPool.getSafePoolSize(2*60*1000 + POOL_CHECK_DELAY); if (safePoolSize < requestedPoolSize) { requestMoreTunnels(requestedPoolSize-safePoolSize); } diff --git a/router/java/src/net/i2p/router/tunnelmanager/RequestTunnelJob.java b/router/java/src/net/i2p/router/tunnelmanager/RequestTunnelJob.java index 85e3dc0cc06b2708dd5235a7be064a137efb9fd4..f87215c2806b1994394639277fe004027b440eed 100644 --- a/router/java/src/net/i2p/router/tunnelmanager/RequestTunnelJob.java +++ b/router/java/src/net/i2p/router/tunnelmanager/RequestTunnelJob.java @@ -260,8 +260,8 @@ public class RequestTunnelJob extends JobImpl { _log.info("Sending tunnel create to " + _target.getIdentity().getHash().toBase64() + " to inbound gateway " + _inboundGateway.getGateway().toBase64() + " : " + _inboundGateway.getTunnelId().getTunnelId()); - ReplyJob onReply = new Success(_participant, _wrappedKey, _wrappedTags, _wrappedTo); - Job onFail = new Failure(_participant); + ReplyJob onReply = new Success(_participant, _wrappedKey, _wrappedTags, _wrappedTo, _inboundGateway.getTunnelId(), _outboundTunnel); + Job onFail = new Failure(_participant, _inboundGateway.getTunnelId(), _outboundTunnel); MessageSelector selector = new Selector(_participant); SendTunnelMessageJob j = new SendTunnelMessageJob(getContext(), _garlicMessage, _outboundTunnel, _target.getIdentity().getHash(), @@ -550,9 +550,11 @@ public class RequestTunnelJob extends JobImpl { private SessionKey _wrappedKey; private Set _wrappedTags; private PublicKey _wrappedTo; + private TunnelId _replyTunnelId; + private TunnelId _outboundTunnelId; private long _started; - public Success(TunnelInfo tunnel, SessionKey wrappedKey, Set wrappedTags, PublicKey wrappedTo) { + public Success(TunnelInfo tunnel, SessionKey wrappedKey, Set wrappedTags, PublicKey wrappedTo, TunnelId replyTunnelId, TunnelId outboundTunnelId) { super(RequestTunnelJob.this.getContext()); _tunnel = tunnel; _messages = new LinkedList(); @@ -560,6 +562,8 @@ public class RequestTunnelJob extends JobImpl { _wrappedKey = wrappedKey; _wrappedTags = wrappedTags; _wrappedTo = wrappedTo; + _replyTunnelId = replyTunnelId; + _outboundTunnelId = outboundTunnelId; _started = getContext().clock().now(); } @@ -644,10 +648,14 @@ public class RequestTunnelJob extends JobImpl { private class Failure extends JobImpl { private TunnelInfo _tunnel; + private TunnelId _outboundTunnelId; + private TunnelId _replyTunnelId; private long _started; - public Failure(TunnelInfo tunnel) { + public Failure(TunnelInfo tunnel, TunnelId replyTunnelId, TunnelId outboundTunnelId) { super(RequestTunnelJob.this.getContext()); _tunnel = tunnel; + _replyTunnelId = replyTunnelId; + _outboundTunnelId = outboundTunnelId; _started = getContext().clock().now(); } @@ -669,6 +677,11 @@ public class RequestTunnelJob extends JobImpl { // perhaps not an explicit reject, but an implicit one (due to dropped messages, tunnel failure, etc) getContext().profileManager().tunnelRejected(_tunnel.getThisHop(), responseTime, false); getContext().profileManager().messageFailed(_tunnel.getThisHop()); + + // one (or both) of the tunnels used to send the request / receive a reply failed + _pool.tunnelFailed(_replyTunnelId); + _pool.tunnelFailed(_outboundTunnelId); + Failure.this.getContext().statManager().updateFrequency("tunnel.buildFailFrequency"); fail(); } diff --git a/router/java/src/net/i2p/router/tunnelmanager/TunnelPool.java b/router/java/src/net/i2p/router/tunnelmanager/TunnelPool.java index 53149209c4e67299931a72a04cc51d09e8020ba0..50a8adbb0efbf425db68b2515ce0bc4b90008cb5 100644 --- a/router/java/src/net/i2p/router/tunnelmanager/TunnelPool.java +++ b/router/java/src/net/i2p/router/tunnelmanager/TunnelPool.java @@ -524,13 +524,22 @@ class TunnelPool { return found; } + private static final int MAX_FAILURES_PER_TUNNEL = 2; + public void tunnelFailed(TunnelId id) { if (!_isLive) return; - if (_log.shouldLog(Log.WARN)) - _log.warn("Tunnel " + id + " marked as not ready, since it /failed/", new Exception("Failed tunnel")); TunnelInfo info = getTunnelInfo(id); if (info == null) return; + int failures = info.incrementFailures(); + if (failures <= MAX_FAILURES_PER_TUNNEL) { + if (_log.shouldLog(Log.INFO)) + _log.info("Tunnel " + id + " failure " + failures + ", but not fatal yet"); + return; + } + + if (_log.shouldLog(Log.WARN)) + _log.warn("Tunnel " + id + " marked as not ready, since it /failed/", new Exception("Failed tunnel")); _context.messageHistory().tunnelFailed(info.getTunnelId()); info.setIsReady(false); Hash us = _context.routerHash();