diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PeerHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/PeerHelper.java index 0e6553324..6074aec0b 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PeerHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PeerHelper.java @@ -29,6 +29,7 @@ public class PeerHelper { public String getPeerSummary() { try { _context.commSystem().renderStatusHTML(_out); + _context.bandwidthLimiter().renderStatusHTML(_out); } catch (IOException ioe) { ioe.printStackTrace(); } diff --git a/history.txt b/history.txt index 3805714ed..2f714c645 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,14 @@ -$Id: history.txt,v 1.306 2005/10/25 14:13:54 jrandom Exp $ +$Id: history.txt,v 1.307 2005/10/28 17:26:55 jrandom Exp $ + +2005-10-29 jrandom + * Improved the bandwidth throtting on tunnel participation, especially for + low bandwidth peers. + * Improved failure handling in SSU with proactive reestablishment of + failing idle peers, and rather than shitlisting a peer who failed too + much, drop the SSU session and allow a new attempt (which, if it fails, + will cause a shitlisting) + * Clarify the cause of the shitlist on the profiles page, and include + bandwidth limiter info at the bottom of the peers page. 2005-10-26 jrandom * In Syndie, propogate the subject and tags in a reply, and show the parent diff --git a/installer/resources/i2ptunnel.config b/installer/resources/i2ptunnel.config index 96c4e61d5..377551c35 100644 --- a/installer/resources/i2ptunnel.config +++ b/installer/resources/i2ptunnel.config @@ -26,7 +26,7 @@ tunnel.1.i2cpPort=7654 tunnel.1.option.inbound.nickname=shared clients tunnel.1.option.outbound.nickname=shared clients tunnel.1.option.i2p.streaming.connectDelay=1000 -tunnel.1.option.i2p.maxWindowSize=1 +tunnel.1.option.i2p.streaming.maxWindowSize=1 tunnel.1.startOnLoad=true # I2P's cvs server diff --git a/router/java/src/net/i2p/router/RouterThrottleImpl.java b/router/java/src/net/i2p/router/RouterThrottleImpl.java index 25ffbaa5c..a5465439f 100644 --- a/router/java/src/net/i2p/router/RouterThrottleImpl.java +++ b/router/java/src/net/i2p/router/RouterThrottleImpl.java @@ -50,6 +50,8 @@ class RouterThrottleImpl implements RouterThrottle { _context.statManager().createRateStat("router.throttleTunnelProbTooFast", "How many tunnels beyond the previous 1h average are we participating in when we throttle?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); _context.statManager().createRateStat("router.throttleTunnelProbTestSlow", "How slow are our tunnel tests when our average exceeds the old average and we throttle?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); _context.statManager().createRateStat("router.throttleTunnelBandwidthExceeded", "How much bandwidth is allocated when we refuse due to bandwidth allocation?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); + _context.statManager().createRateStat("router.throttleTunnelBytesAllowed", "How many bytes are allowed to be sent when we get a tunnel request (period is how many are currently allocated)?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); + _context.statManager().createRateStat("router.throttleTunnelBytesUsed", "Used Bps at request (period = max KBps)?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); } public boolean acceptNetworkMessage() { @@ -212,7 +214,13 @@ class RouterThrottleImpl implements RouterThrottle { r = null; if (rs != null) r = rs.getRate(10*60*1000); - double bytesAllocated = (r != null ? r.getCurrentTotalValue() * 1024 : 0); + double messagesPerTunnel = (r != null ? r.getAverageValue() : 0d); + if (messagesPerTunnel < DEFAULT_MESSAGES_PER_TUNNEL_ESTIMATE) + messagesPerTunnel = DEFAULT_MESSAGES_PER_TUNNEL_ESTIMATE; + int participatingTunnels = (r != null ? (int) (r.getLastEventCount() + r.getCurrentEventCount()) : 0); + if (participatingTunnels <= 0) + participatingTunnels = _context.tunnelManager().getParticipatingCount(); + double bytesAllocated = messagesPerTunnel * participatingTunnels * 1024; if (!allowTunnel(bytesAllocated, numTunnels)) { _context.statManager().addRateData("router.throttleTunnelBandwidthExceeded", (long)bytesAllocated, 0); @@ -227,6 +235,8 @@ class RouterThrottleImpl implements RouterThrottle { return TUNNEL_ACCEPT; } + private static final int DEFAULT_MESSAGES_PER_TUNNEL_ESTIMATE = 600; // 1KBps + /** * with bytesAllocated already accounted for across the numTunnels existing * tunnels we have agreed to, can we handle another tunnel with our existing @@ -234,26 +244,33 @@ class RouterThrottleImpl implements RouterThrottle { * */ private boolean allowTunnel(double bytesAllocated, int numTunnels) { - long bytesAllowed = getBytesAllowed(); - - bytesAllowed *= getSharePercentage(); - - double bytesPerTunnel = (numTunnels > 0 ? bytesAllocated / numTunnels : 0); - double toAllocate = (numTunnels > 0 ? bytesPerTunnel * (numTunnels + 1) : 0); - - double pctFull = toAllocate / bytesAllowed; + int maxKBps = Math.min(_context.bandwidthLimiter().getOutboundKBytesPerSecond(), _context.bandwidthLimiter().getInboundKBytesPerSecond()); + int used = (int)Math.max(_context.bandwidthLimiter().getSendBps(), _context.bandwidthLimiter().getReceiveBps()); + int availBps = (int)(((maxKBps*1024) - used) * getSharePercentage()); + + _context.statManager().addRateData("router.throttleTunnelBytesUsed", used, maxKBps); + _context.statManager().addRateData("router.throttleTunnelBytesAllowed", availBps, (long)bytesAllocated); + + if (maxKBps <= 8) { + // lets be more conservative for dialup users and assume 1KBps per tunnel + return ( (numTunnels + 1)*1024 < availBps); + } + + double growthFactor = ((double)(numTunnels+1))/(double)numTunnels; + double toAllocate = (numTunnels > 0 ? bytesAllocated * growthFactor : 0); double allocatedKBps = toAllocate / (10 * 60 * 1024); + double pctFull = allocatedKBps / availBps; - if (pctFull < 1.0) { // (_context.random().nextInt(100) > 100 * pctFull) { + if ( (pctFull < 1.0) && (pctFull >= 0.0) ) { // (_context.random().nextInt(100) > 100 * pctFull) { if (_log.shouldLog(Log.DEBUG)) - _log.debug("Probabalistically allowing the tunnel w/ " + pctFull + " of our " + bytesAllowed - + "bytes/" + allocatedKBps + "KBps allocated through " + numTunnels + " tunnels"); + _log.debug("Probabalistically allowing the tunnel w/ " + pctFull + " of our " + availBps + + "Bps/" + allocatedKBps + "KBps allocated through " + numTunnels + " tunnels"); return true; } else { if (_log.shouldLog(Log.DEBUG)) - _log.debug("Rejecting the tunnel w/ " + pctFull + " of our " + bytesAllowed - + "bytes allowed (" + toAllocate + "bytes / " + allocatedKBps + _log.debug("Rejecting the tunnel w/ " + pctFull + " of our " + availBps + + "Bps allowed (" + toAllocate + "bytes / " + allocatedKBps + "KBps) through " + numTunnels + " tunnels"); return false; } @@ -268,7 +285,11 @@ class RouterThrottleImpl implements RouterThrottle { String pct = _context.getProperty(PROP_BANDWIDTH_SHARE_PERCENTAGE, "0.8"); if (pct != null) { try { - return Double.parseDouble(pct); + double d = Double.parseDouble(pct); + if (d > 1) + return d/100d; // *cough* sometimes its 80 instead of .8 (!stab jrandom) + else + return d; } catch (NumberFormatException nfe) { if (_log.shouldLog(Log.INFO)) _log.info("Unable to get the share percentage"); @@ -276,50 +297,7 @@ class RouterThrottleImpl implements RouterThrottle { } return 0.8; } - - /** - * BytesPerSecond that we can pass along data - */ - private long getBytesAllowed() { - String kbpsOutStr = _context.getProperty("i2np.bandwidth.outboundKBytesPerSecond"); - long kbpsOut = -1; - if (kbpsOutStr != null) { - try { - kbpsOut = Integer.parseInt(kbpsOutStr); - } catch (NumberFormatException nfe) { - if (_log.shouldLog(Log.INFO)) - _log.info("Unable to get the bytes allowed (outbound)"); - } - } - - String kbpsInStr = _context.getProperty("i2np.bandwidth.inboundKBytesPerSecond"); - long kbpsIn = -1; - if (kbpsInStr != null) { - try { - kbpsIn = Integer.parseInt(kbpsInStr); - } catch (NumberFormatException nfe) { - if (_log.shouldLog(Log.INFO)) - _log.info("Unable to get the bytes allowed (inbound)"); - } - } - - // whats our choke? - long kbps = (kbpsOut > kbpsIn ? kbpsIn : kbpsOut); - - if (kbps <= 0) { - try { - kbps = Integer.parseInt(_context.getProperty(PROP_DEFAULT_KBPS_THROTTLE, "64")); // absurd - } catch (NumberFormatException nfe) { - kbps = 64; - } - } - - return kbps - * 60 // per minute - * 10 // per 10 minute period - * 1024; // bytes; - } - + /** dont ever probabalistically throttle tunnels if we have less than this many */ private int getMinThrottleTunnels() { try { diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index db0d48ecb..b64a12c2e 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -15,9 +15,9 @@ import net.i2p.CoreVersion; * */ public class RouterVersion { - public final static String ID = "$Revision: 1.277 $ $Date: 2005/10/25 14:13:53 $"; + public final static String ID = "$Revision: 1.278 $ $Date: 2005/10/28 17:26:53 $"; public final static String VERSION = "0.6.1.3"; - public final static long BUILD = 8; + public final static long BUILD = 9; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/Shitlist.java b/router/java/src/net/i2p/router/Shitlist.java index 5a7343895..8dbc1c1f5 100644 --- a/router/java/src/net/i2p/router/Shitlist.java +++ b/router/java/src/net/i2p/router/Shitlist.java @@ -75,7 +75,7 @@ public class Shitlist { Date oldDate = (Date)_shitlist.put(peer, new Date(_context.clock().now() + period)); wasAlready = (null == oldDate); if (reason != null) { - if (!wasAlready) + if (!wasAlready || (!_shitlistCause.containsKey(peer)) ) _shitlistCause.put(peer, reason); } else { _shitlistCause.remove(peer); diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java index 2b2e1dd47..4f5fa7235 100644 --- a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java +++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java @@ -100,6 +100,9 @@ public class FIFOBandwidthLimiter { public float getSendBps() { return _sendBps; } public float getReceiveBps() { return _recvBps; } + public int getOutboundKBytesPerSecond() { return _refiller.getOutboundKBytesPerSecond(); } + public int getInboundKBytesPerSecond() { return _refiller.getInboundKBytesPerSecond(); } + public void reinitialize() { _pendingInboundRequests.clear(); _pendingOutboundRequests.clear(); diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java index 4bf31c3db..3af79a8e0 100644 --- a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java +++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java @@ -328,4 +328,7 @@ class FIFOBandwidthRefiller implements Runnable { _limiter.setOutboundBurstBytes(DEFAULT_BURST_SECONDS * _outboundBurstKBytesPerSecond * 1024); } } + + int getOutboundKBytesPerSecond() { return _outboundKBytesPerSecond; } + int getInboundKBytesPerSecond() { return _inboundKBytesPerSecond; } } \ No newline at end of file diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java index 0fe0ccc6b..5bb01c20f 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java @@ -65,6 +65,8 @@ public class PeerState { private long _currentReceiveSecond; /** when did we last send them a packet? */ private long _lastSendTime; + /** when did we last send them a message that was ACKed */ + private long _lastSendFullyTime; /** when did we last receive a packet from them? */ private long _lastReceiveTime; /** how many consecutive messages have we sent and not received an ACK to */ @@ -270,6 +272,8 @@ public class PeerState { public long getCurrentReceiveSecond() { return _currentReceiveSecond; } /** when did we last send them a packet? */ public long getLastSendTime() { return _lastSendTime; } + /** when did we last send them a message that was ACKed? */ + public long getLastSendFullyTime() { return _lastSendFullyTime; } /** when did we last receive a packet from them? */ public long getLastReceiveTime() { return _lastReceiveTime; } /** how many seconds have we sent packets without any ACKs received? */ @@ -658,6 +662,7 @@ public class PeerState { if (_sendWindowBytes > MAX_SEND_WINDOW_BYTES) _sendWindowBytes = MAX_SEND_WINDOW_BYTES; _lastReceiveTime = _context.clock().now(); + _lastSendFullyTime = _lastReceiveTime; if (true) { if (_sendWindowBytesRemaining + bytesACKed <= _sendWindowBytes) diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index bf00cc1d1..c674de151 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -147,6 +147,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _context.statManager().createRateStat("udp.statusUnknown", "How many times the peer test returned an unknown result", "udp", new long[] { 5*60*1000, 20*60*1000, 60*60*1000, 24*60*60*1000 }); _context.statManager().createRateStat("udp.addressTestInsteadOfUpdate", "How many times we fire off a peer test of ourselves instead of adjusting our own reachable address?", "udp", new long[] { 1*60*1000, 20*60*1000, 60*60*1000, 24*60*60*1000 }); _context.statManager().createRateStat("udp.addressUpdated", "How many times we adjust our own reachable IP address", "udp", new long[] { 1*60*1000, 20*60*1000, 60*60*1000, 24*60*60*1000 }); + _context.statManager().createRateStat("udp.proactiveReestablish", "How long a session was idle for when we proactively reestablished it", "udp", new long[] { 1*60*1000, 20*60*1000, 60*60*1000, 24*60*60*1000 }); } public void startup() { @@ -661,6 +662,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority return (pref != null) && "true".equals(pref); } + private static final int MAX_IDLE_TIME = 60*1000; + public String getStyle() { return STYLE; } public void send(OutNetMessage msg) { if (msg == null) return; @@ -668,13 +671,24 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority if (msg.getTarget().getIdentity() == null) return; Hash to = msg.getTarget().getIdentity().calculateHash(); - if (getPeerState(to) != null) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending outbound message to an established peer: " + to.toBase64()); + PeerState peer = getPeerState(to); + if (peer != null) { + long lastSend = peer.getLastSendFullyTime(); + long lastRecv = peer.getLastReceiveTime(); + long now = _context.clock().now(); + if ( (lastSend > 0) && (lastRecv > 0) ) { + if ( (now - lastSend > MAX_IDLE_TIME) && + (now - lastRecv > MAX_IDLE_TIME) && + (peer.getConsecutiveFailedSends() > 0) ) { + // peer is waaaay idle, drop the con and queue it up as a new con + dropPeer(peer, false); + _establisher.establish(msg); + _context.statManager().addRateData("udp.proactiveReestablish", now-lastSend, now-peer.getKeyEstablishedTime()); + return; + } + } _outboundMessages.add(msg); } else { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending outbound message to an unestablished peer: " + to.toBase64()); _establisher.establish(msg); } } @@ -836,7 +850,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority if (_log.shouldLog(Log.WARN)) _log.warn("Consecutive failure #" + consecutive + " sending to " + msg.getPeer()); if (consecutive > MAX_CONSECUTIVE_FAILED) - dropPeer(msg.getPeer()); + dropPeer(msg.getPeer(), false); } failed(msg.getMessage()); }