diff --git a/history.txt b/history.txt index 8a30c2ea3..600dd3e6a 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,13 @@ -$Id: history.txt,v 1.167 2005/03/07 21:45:14 jrandom Exp $ +$Id: history.txt,v 1.168 2005/03/11 17:23:41 jrandom Exp $ + +2005-03-14 jrandom + * New strict speed calculator that goes off the actual number of messages + verifiably sent through the peer by way of tunnels. Initially, this only + contains the successful message count on inbound tunnels, but may be + augmented later to include verified outbound messages, peers queried in + the netDb, etc. The speed calculation decays quickly, but should give + a better differential than the previous stat (both values are shown on + the /profiles.jsp page) 2005-03-11 jrandom * Rather than the fixed resend timeout floor (10s), use 10s+RTT as the diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java index f596de7de..1eab8727c 100644 --- a/router/java/src/net/i2p/router/RouterContext.java +++ b/router/java/src/net/i2p/router/RouterContext.java @@ -18,6 +18,7 @@ import net.i2p.router.peermanager.ProfileManagerImpl; import net.i2p.router.peermanager.ProfileOrganizer; import net.i2p.router.peermanager.ReliabilityCalculator; import net.i2p.router.peermanager.SpeedCalculator; +import net.i2p.router.peermanager.StrictSpeedCalculator; import net.i2p.router.transport.CommSystemFacadeImpl; import net.i2p.router.transport.FIFOBandwidthLimiter; import net.i2p.router.transport.OutboundMessageRegistry; @@ -62,6 +63,7 @@ public class RouterContext extends I2PAppContext { private Calculator _speedCalc; private Calculator _reliabilityCalc; private Calculator _capacityCalc; + private Calculator _oldSpeedCalc; private static List _contexts = new ArrayList(1); @@ -114,7 +116,8 @@ public class RouterContext extends I2PAppContext { _throttle = new RouterDoSThrottle(this); _isFailingCalc = new IsFailingCalculator(this); _integrationCalc = new IntegrationCalculator(this); - _speedCalc = new SpeedCalculator(this); + _speedCalc = new StrictSpeedCalculator(this); + _oldSpeedCalc = new SpeedCalculator(this); _reliabilityCalc = new ReliabilityCalculator(this); _capacityCalc = new CapacityCalculator(this); } @@ -248,6 +251,7 @@ public class RouterContext extends I2PAppContext { public Calculator integrationCalculator() { return _integrationCalc; } /** how do we rank the speed of profiles? */ public Calculator speedCalculator() { return _speedCalc; } + public Calculator oldSpeedCalculator() { return _oldSpeedCalc; } /** how do we rank the reliability of profiles? */ public Calculator reliabilityCalculator() { return _reliabilityCalc; } /** how do we rank the capacity of profiles? */ diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index ccb0ede39..1f88e1f61 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.161 $ $Date: 2005/03/07 21:45:15 $"; + public final static String ID = "$Revision: 1.162 $ $Date: 2005/03/11 17:23:41 $"; public final static String VERSION = "0.5.0.2"; - public final static long BUILD = 2; + public final static long BUILD = 3; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/peermanager/PeerProfile.java b/router/java/src/net/i2p/router/peermanager/PeerProfile.java index 242782dd8..4dadb6c8b 100644 --- a/router/java/src/net/i2p/router/peermanager/PeerProfile.java +++ b/router/java/src/net/i2p/router/peermanager/PeerProfile.java @@ -38,6 +38,7 @@ public class PeerProfile { private double _reliabilityValue; private double _capacityValue; private double _integrationValue; + private double _oldSpeedValue; private boolean _isFailing; // good vs bad behavior private TunnelHistory _tunnelHistory; @@ -186,6 +187,7 @@ public class PeerProfile { * */ public double getSpeedValue() { return _speedValue; } + public double getOldSpeedValue() { return _oldSpeedValue; } /** * How likely are they to stay up and pass on messages over the next few minutes. * Positive numbers means more likely, negative numbers means its probably not @@ -287,6 +289,7 @@ public class PeerProfile { _tunnelHistory.coalesceStats(); _speedValue = calculateSpeed(); + _oldSpeedValue = calculateOldSpeed(); _reliabilityValue = calculateReliability(); _capacityValue = calculateCapacity(); _integrationValue = calculateIntegration(); @@ -297,6 +300,7 @@ public class PeerProfile { } private double calculateSpeed() { return _context.speedCalculator().calc(this); } + private double calculateOldSpeed() { return _context.oldSpeedCalculator().calc(this); } private double calculateReliability() { return _context.reliabilityCalculator().calc(this); } private double calculateCapacity() { return _context.capacityCalculator().calc(this); } private double calculateIntegration() { return _context.integrationCalculator().calc(this); } diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java index 8ec6a937f..aa9cd0268 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java +++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizerRenderer.java @@ -102,7 +102,8 @@ class ProfileOrganizerRenderer { } if (isIntegrated) buf.append(", Integrated"); - buf.append("").append(num(prof.getSpeedValue())).append(""); + buf.append("").append(num(prof.getSpeedValue())); + buf.append('/').append(num(prof.getOldSpeedValue())).append(""); buf.append("").append(num(prof.getCapacityValue())).append(""); buf.append("").append(num(prof.getIntegrationValue())).append(""); buf.append("").append(prof.getIsFailing()).append(""); diff --git a/router/java/src/net/i2p/router/peermanager/StrictSpeedCalculator.java b/router/java/src/net/i2p/router/peermanager/StrictSpeedCalculator.java new file mode 100644 index 000000000..f8527e40a --- /dev/null +++ b/router/java/src/net/i2p/router/peermanager/StrictSpeedCalculator.java @@ -0,0 +1,90 @@ +package net.i2p.router.peermanager; + +import net.i2p.stat.Rate; +import net.i2p.stat.RateStat; +import net.i2p.router.RouterContext; +import net.i2p.util.Log; + +/** + * Simple speed calculator that just counts how many messages go through the + * tunnel. + * + */ +public class StrictSpeedCalculator extends Calculator { + private Log _log; + private RouterContext _context; + + public StrictSpeedCalculator(RouterContext context) { + _context = context; + _log = context.logManager().getLog(StrictSpeedCalculator.class); + } + + public double calc(PeerProfile profile) { + return countSuccesses(profile); + } + private double countSuccesses(PeerProfile profile) { + RateStat success = profile.getTunnelHistory().getProcessSuccessRate(); + RateStat failure = profile.getTunnelHistory().getProcessFailureRate(); + return messagesPerMinute(success, failure); + } + private double messagesPerMinute(RateStat success, RateStat failure) { + double rv = 0.0d; + if (success != null) { + Rate rate = null; + long periods[] = success.getPeriods(); + for (int i = 0; i < periods.length; i++) { + rate = success.getRate(periods[i]); + if ( (rate != null) && (rate.getCurrentTotalValue() > 0) ) + break; + } + + double value = rate.getCurrentTotalValue(); + value += rate.getLastTotalValue(); + rv = value * 10.0d * 60.0d * 1000.0d / (double)rate.getPeriod(); + + // if any of the messages are getting fragmented and cannot be + // handled, penalize like crazy + Rate fail = failure.getRate(rate.getPeriod()); + if (fail.getCurrentTotalValue() > 0) + rv /= fail.getCurrentTotalValue(); + } + return rv; + } + + /* + public double calc(PeerProfile profile) { + double successCount = countSuccesses(profile); + double failureCount = countFailures(profile); + + double rv = successCount - 5*failureCount; + if (rv < 0) + rv = 0; + return rv; + } + private double countSuccesses(PeerProfile profile) { + RateStat success = profile.getTunnelHistory().getProcessSuccessRate(); + return messagesPerMinute(success); + } + private double countFailures(PeerProfile profile) { + RateStat failure = profile.getTunnelHistory().getProcessFailureRate(); + return messagesPerMinute(failure); + } + private double messagesPerMinute(RateStat stat) { + double rv = 0.0d; + if (stat != null) { + Rate rate = null; + long periods[] = stat.getPeriods(); + for (int i = 0; i < periods.length; i++) { + rate = stat.getRate(periods[i]); + if ( (rate != null) && (rate.getCurrentTotalValue() > 0) ) + break; + } + + double value = rate.getCurrentTotalValue(); + value += rate.getLastTotalValue(); + rv = value * 60.0d * 1000.0d / (double)rate.getPeriod(); + } + return rv; + } + */ +} diff --git a/router/java/src/net/i2p/router/peermanager/TunnelHistory.java b/router/java/src/net/i2p/router/peermanager/TunnelHistory.java index 8b885fd76..c50d19f8c 100644 --- a/router/java/src/net/i2p/router/peermanager/TunnelHistory.java +++ b/router/java/src/net/i2p/router/peermanager/TunnelHistory.java @@ -26,6 +26,8 @@ public class TunnelHistory { private volatile long _lastFailed; private RateStat _rejectRate; private RateStat _failRate; + private RateStat _processSuccessRate; + private RateStat _processFailureRate; private String _statGroup; /** probabalistic tunnel rejection due to a flood of requests */ @@ -47,8 +49,12 @@ public class TunnelHistory { private void createRates(String statGroup) { _rejectRate = new RateStat("tunnelHistory.rejectRate", "How often does this peer reject a tunnel request?", statGroup, new long[] { 60*1000l, 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); _failRate = new RateStat("tunnelHistory.failRate", "How often do tunnels this peer accepts fail?", statGroup, new long[] { 60*1000l, 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); + _processSuccessRate = new RateStat("tunnelHistory.processSuccessRate", "How many messages does a tunnel process?", statGroup, new long[] { 5*60*1000l, 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); + _processFailureRate = new RateStat("tunnelHistory.processfailureRate", "How many messages does a tunnel fail?", statGroup, new long[] { 5*60*1000l, 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); _rejectRate.setStatLog(_context.statManager().getStatLog()); _failRate.setStatLog(_context.statManager().getStatLog()); + _processSuccessRate.setStatLog(_context.statManager().getStatLog()); + _processFailureRate.setStatLog(_context.statManager().getStatLog()); } /** total tunnels the peer has agreed to participate in */ @@ -70,6 +76,13 @@ public class TunnelHistory { /** when the last tunnel the peer participated in failed */ public long getLastFailed() { return _lastFailed; } + public void incrementProcessed(int processedSuccessfully, int failedProcessing) { + if (processedSuccessfully > 0) + _processSuccessRate.addData(processedSuccessfully, 0); + if (failedProcessing > 0) + _processFailureRate.addData(failedProcessing, 0); + } + public void incrementAgreedTo() { _lifetimeAgreedTo++; _lastAgreedTo = _context.clock().now(); @@ -113,12 +126,16 @@ public class TunnelHistory { public RateStat getRejectionRate() { return _rejectRate; } public RateStat getFailedRate() { return _failRate; } + public RateStat getProcessSuccessRate() { return _processSuccessRate; } + public RateStat getProcessFailureRate() { return _processFailureRate; } public void coalesceStats() { if (_log.shouldLog(Log.DEBUG)) _log.debug("Coallescing stats"); _rejectRate.coalesceStats(); _failRate.coalesceStats(); + _processFailureRate.coalesceStats(); + _processSuccessRate.coalesceStats(); } private final static String NL = System.getProperty("line.separator"); @@ -140,7 +157,9 @@ public class TunnelHistory { add(buf, "lifetimeRejected", _lifetimeRejected, "How many tunnels has the peer ever refused to participate in?"); out.write(buf.toString().getBytes()); _rejectRate.store(out, "tunnelHistory.rejectRate"); - _rejectRate.store(out, "tunnelHistory.failRate"); + _failRate.store(out, "tunnelHistory.failRate"); + _processSuccessRate.store(out, "tunnelHistory.processSuccessRate"); + _processFailureRate.store(out, "tunnelHistory.processFailureRate"); } private void add(StringBuffer buf, String name, long val, String description) { @@ -162,9 +181,15 @@ public class TunnelHistory { _rejectRate.load(props, "tunnelHistory.rejectRate", true); if (_log.shouldLog(Log.DEBUG)) _log.debug("Loading tunnelHistory.rejectRate"); - _rejectRate.load(props, "tunnelHistory.failRate", true); + _failRate.load(props, "tunnelHistory.failRate", true); if (_log.shouldLog(Log.DEBUG)) _log.debug("Loading tunnelHistory.failRate"); + _processFailureRate.load(props, "tunnelHistory.processFailureRate", true); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Loading tunnelHistory.processFailureRate"); + _processSuccessRate.load(props, "tunnelHistory.processSuccessRate", true); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Loading tunnelHistory.processSuccessRate"); } catch (IllegalArgumentException iae) { _log.warn("TunnelHistory rates are corrupt, resetting", iae); createRates(_statGroup); diff --git a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java index b20867e80..bdaa5669a 100644 --- a/router/java/src/net/i2p/router/tunnel/FragmentHandler.java +++ b/router/java/src/net/i2p/router/tunnel/FragmentHandler.java @@ -29,6 +29,8 @@ public class FragmentHandler { private Log _log; private Map _fragmentedMessages; private DefragmentedReceiver _receiver; + private int _completed; + private int _failed; /** don't wait more than 60s to defragment the partial message */ private static final long MAX_DEFRAGMENT_TIME = 60*1000; @@ -84,6 +86,9 @@ public class FragmentHandler { } } + public int getCompleteCount() { return _completed; } + public int getFailedCount() { return _failed; } + private static final ByteCache _validateCache = ByteCache.getInstance(512, TrivialPreprocessor.PREPROCESSED_SIZE); /** @@ -312,6 +317,7 @@ public class FragmentHandler { } private void receiveComplete(FragmentedMessage msg) { + _completed++; try { byte data[] = msg.toByteArray(); if (_log.shouldLog(Log.DEBUG)) @@ -362,6 +368,7 @@ public class FragmentHandler { removed = (null != _fragmentedMessages.remove(new Long(_msg.getMessageId()))); } if (removed && !_msg.getReleased()) { + _failed++; noteFailure(_msg.getMessageId()); if (_log.shouldLog(Log.ERROR)) _log.error("Dropped failed fragmented message: " + _msg); diff --git a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java index 48912c0ea..134ed050e 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java @@ -14,6 +14,7 @@ import net.i2p.data.TunnelId; import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.TunnelDataMessage; import net.i2p.data.i2np.TunnelGatewayMessage; +import net.i2p.router.peermanager.PeerProfile; import net.i2p.router.JobImpl; import net.i2p.router.Router; import net.i2p.router.Service; @@ -248,22 +249,37 @@ public class TunnelDispatcher implements Service { TunnelId recvId = cfg.getConfig(cfg.getLength()-1).getReceiveTunnel(); if (_log.shouldLog(Log.DEBUG)) _log.debug("removing our own inbound " + cfg); - boolean removed = false; + TunnelParticipant participant = null; synchronized (_participants) { - removed = (null != _participants.remove(recvId)); + participant = (TunnelParticipant)_participants.remove(recvId); } - if (!removed) { + if (participant == null) { synchronized (_inboundGateways) { _inboundGateways.remove(recvId); } + } else { + // update stats based off getCompleteCount() + getFailedCount() + for (int i = 0; i < cfg.getLength(); i++) { + Hash peer = cfg.getPeer(i); + PeerProfile profile = _context.profileOrganizer().getProfile(peer); + if (profile != null) { + int ok = participant.getCompleteCount(); + int fail = participant.getFailedCount(); + profile.getTunnelHistory().incrementProcessed(ok, fail); + } + } } } else { if (_log.shouldLog(Log.DEBUG)) _log.debug("removing our own outbound " + cfg); TunnelId outId = cfg.getConfig(0).getSendTunnel(); + TunnelGateway gw = null; synchronized (_outboundGateways) { - _outboundGateways.remove(outId); + gw = (TunnelGateway)_outboundGateways.remove(outId); } + if (gw != null) { + // update stats based on gw.getMessagesSent() + } } } diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java index 55244f4c6..25642da4a 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java @@ -42,6 +42,7 @@ public class TunnelGateway { private long _lastFlush; private int _flushFrequency; private DelayedFlush _delayedFlush; + private int _messagesSent; /** * @param preprocessor this pulls Pending messages off a list, builds some @@ -58,6 +59,7 @@ public class TunnelGateway { _preprocessor = preprocessor; _sender = sender; _receiver = receiver; + _messagesSent = 0; _flushFrequency = 500; _delayedFlush = new DelayedFlush(); _lastFlush = _context.clock().now(); @@ -82,6 +84,7 @@ public class TunnelGateway { * @param toTunnel tunnel to send to after the endpoint (or null for endpoint or router processing) */ public void add(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) { + _messagesSent++; boolean delayedFlush = false; Pending cur = new Pending(msg, toRouter, toTunnel); @@ -105,6 +108,8 @@ public class TunnelGateway { } } + public int getMessagesSent() { return _messagesSent; } + public interface Sender { /** * Take the preprocessed data containing zero or more fragments, encrypt diff --git a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java index 5f7a39280..1a691ecae 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java @@ -98,6 +98,19 @@ public class TunnelParticipant { } } + public int getCompleteCount() { + if (_handler != null) + return _handler.getCompleteCount(); + else + return 0; + } + public int getFailedCount() { + if (_handler != null) + return _handler.getFailedCount(); + else + return 0; + } + private class DefragmentedHandler implements FragmentHandler.DefragmentedReceiver { public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) { if (_log.shouldLog(Log.DEBUG))