diff --git a/history.txt b/history.txt index 258cb0f86..825eda164 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,21 @@ +2011-10-28 zzz + * BuildHandler: Move inbound request handling to its own thread(s) + (ticket #542, see also http://zzz.i2p/topics/996) + * CapacityCalculator: Small boost for connected peers, new peers, and + same-country peers; deduct for recently-unreachable peers + * DecayingBloomFilter: Whups fix NPE from previous checkin if log=INFO + * NTCP: Reduce min idle time + * SSU: + - Increase default max connections again + - Reduce min idle time + - Separate out introducer pinger from introducer selection + so it can be run separately and more often + - Only ping introducers if we need them + * Tunnels: + - Reduce exploratory tunnel quantity if build success rate + is very low, but may disable this later + - Try rebuilding same tunnel (some of the time) + 2011-10-25 zzz * BloomSHA1, DecayingBloomFilter: - Refactor for concurrent, at some small risk of false negatives diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 87e5bffca..282c18b42 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 1; + public final static long BUILD = 2; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/TunnelInfo.java b/router/java/src/net/i2p/router/TunnelInfo.java index 678963068..7e06e3463 100644 --- a/router/java/src/net/i2p/router/TunnelInfo.java +++ b/router/java/src/net/i2p/router/TunnelInfo.java @@ -74,4 +74,16 @@ public interface TunnelInfo { public long getVerifiedBytesTransferred(); /** we know for sure that the given number of bytes were sent down the tunnel fully */ public void incrementVerifiedBytesTransferred(int numBytes); + + /** + * Did we reuse this tunnel? + * @since 0.8.11 + */ + public boolean wasReused(); + + /** + * Note that we reused this tunnel + * @since 0.8.11 + */ + public void setReused(); } diff --git a/router/java/src/net/i2p/router/tunnel/TunnelCreatorConfig.java b/router/java/src/net/i2p/router/tunnel/TunnelCreatorConfig.java index b6d1be741..d99cdc5a1 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelCreatorConfig.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelCreatorConfig.java @@ -33,6 +33,7 @@ public class TunnelCreatorConfig implements TunnelInfo { private volatile long _verifiedBytesTransferred; private boolean _failed; private int _failures; + private boolean _reused; public TunnelCreatorConfig(RouterContext ctx, int length, boolean isInbound) { this(ctx, length, isInbound, null); @@ -190,6 +191,18 @@ public class TunnelCreatorConfig implements TunnelInfo { _failures = 0; } + /** + * Did we reuse this tunnel? + * @since 0.8.11 + */ + public boolean wasReused() { return _reused; } + + /** + * Note that we reused this tunnel + * @since 0.8.11 + */ + public void setReused() { _reused = true; } + @Override public String toString() { // H0:1235-->H1:2345-->H2:2345 diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java index 94e0ba0a0..55d62d585 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java @@ -292,7 +292,7 @@ public class TunnelPool { * Used to prevent a zillion of them */ boolean needFallback() { - int needed = _settings.getTotalQuantity(); + int needed = getAdjustedTotalQuantity(); int fallbacks = 0; synchronized (_tunnels) { for (int i = 0; i < _tunnels.size(); i++) { @@ -304,6 +304,45 @@ public class TunnelPool { return true; } + /** + * Return settings.getTotalQuantity, unless this is an exploratory tunnel + * AND exploratory build success rate is less than 1/10, AND total settings + * is greater than 1. Otherwise subtract 1 to help prevent congestion collapse, + * and prevent really unintegrated routers from working too hard. + * We only do this for exploratory as different clients could have different + * length settings. Although I guess inbound and outbound exploratory + * could be different too, and inbound is harder... + * + * @since 0.8.11 + */ + private int getAdjustedTotalQuantity() { + int rv = _settings.getTotalQuantity(); + if (_settings.isExploratory() && rv > 1) { + RateStat e = _context.statManager().getRate("tunnel.buildExploratoryExpire"); + RateStat r = _context.statManager().getRate("tunnel.buildExploratoryReject"); + RateStat s = _context.statManager().getRate("tunnel.buildExploratorySuccess"); + if (e != null && r != null && s != null) { + // 60 min was too long - is 10 min too short? + // By not adding in previous period, this gives us a burst every + // 10 min - is that good or bad? + Rate er = e.getRate(10*60*1000); + Rate rr = r.getRate(10*60*1000); + Rate sr = s.getRate(10*60*1000); + if (er != null && rr != null && sr != null) { + long ec = er.getCurrentEventCount(); + long rc = rr.getCurrentEventCount(); + long sc = sr.getCurrentEventCount(); + long tot = ec + rc + sc; + if (tot >= 10) { + if (1000 * sc / tot <= 1000 / 10) + rv--; + } + } + } + } + return rv; + } + /** list of tunnelInfo instances of tunnels currently being built */ public List listPending() { synchronized (_inProgress) { return new ArrayList(_inProgress); } } @@ -475,7 +514,7 @@ public class TunnelPool { } } - /** noop for outbound */ + /** noop for outbound and exploratory */ void refreshLeaseSet() { if (_settings.isInbound() && (_settings.getDestination() != null) ) { if (_log.shouldLog(Log.DEBUG)) @@ -495,7 +534,7 @@ public class TunnelPool { * */ boolean buildFallback() { - int quantity = _settings.getTotalQuantity(); + int quantity = getAdjustedTotalQuantity(); int usable = 0; synchronized (_tunnels) { usable = _tunnels.size(); @@ -678,7 +717,7 @@ public class TunnelPool { if (!isAlive()) { return 0; } - int wanted = getSettings().getTotalQuantity(); + int wanted = getAdjustedTotalQuantity(); boolean allowZeroHop = ((getSettings().getLength() + getSettings().getLengthVariance()) <= 0); @@ -965,10 +1004,30 @@ public class TunnelPool { TunnelPoolSettings settings = getSettings(); // peers for new tunnel, including us, ENDPOINT FIRST List peers = null; - long expiration = _context.clock().now() + TunnelPoolSettings.DEFAULT_DURATION; + long now = _context.clock().now(); + long expiration = now + TunnelPoolSettings.DEFAULT_DURATION; if (!forceZeroHop) { - peers = _peerSelector.selectPeers(_context, settings); + int len = settings.getLength(); + if (len > 0 && _context.random().nextBoolean()) { + // look for a tunnel to reuse, if the right length and expiring soon + // ignore variance for now. + len++; // us + synchronized (_tunnels) { + for (TunnelInfo ti : _tunnels) { + if (ti.getLength() == len && ti.getExpiration() < now + 3*60*1000 && !ti.wasReused()) { + ti.setReused(); + peers = new ArrayList(len); + // peers list is ordered endpoint first, but cfg.getPeer() is ordered gateway first + for (int i = len - 1; i >= 0; i--) { + peers.add(ti.getPeer(i)); + } + } + } + } + } + if (peers == null) + peers = _peerSelector.selectPeers(_context, settings); if ( (peers == null) || (peers.isEmpty()) ) { // no peers to build the tunnel with, and