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.
This commit is contained in:
jrandom
2005-10-29 21:35:24 +00:00
committed by zzz
parent b5a25801b4
commit 52ace2d695
10 changed files with 84 additions and 70 deletions

View File

@ -29,6 +29,7 @@ public class PeerHelper {
public String getPeerSummary() {
try {
_context.commSystem().renderStatusHTML(_out);
_context.bandwidthLimiter().renderStatusHTML(_out);
} catch (IOException ioe) {
ioe.printStackTrace();
}

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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();

View File

@ -328,4 +328,7 @@ class FIFOBandwidthRefiller implements Runnable {
_limiter.setOutboundBurstBytes(DEFAULT_BURST_SECONDS * _outboundBurstKBytesPerSecond * 1024);
}
}
int getOutboundKBytesPerSecond() { return _outboundKBytesPerSecond; }
int getInboundKBytesPerSecond() { return _inboundKBytesPerSecond; }
}

View File

@ -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)

View File

@ -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());
}