package net.i2p.router.peermanager; import java.io.IOException; import java.io.OutputStream; import java.util.Date; import java.util.Properties; import java.util.concurrent.atomic.AtomicLong; import net.i2p.router.RouterContext; import net.i2p.stat.RateStat; import net.i2p.util.Log; /** * Tunnel related history information * */ public class TunnelHistory { private final RouterContext _context; private final Log _log; private final AtomicLong _lifetimeAgreedTo = new AtomicLong(); private final AtomicLong _lifetimeRejected = new AtomicLong(); private volatile long _lastAgreedTo; private volatile long _lastRejectedCritical; private volatile long _lastRejectedBandwidth; private volatile long _lastRejectedTransient; private volatile long _lastRejectedProbabalistic; private final AtomicLong _lifetimeFailed = new AtomicLong(); private volatile long _lastFailed; private RateStat _rejectRate; private RateStat _failRate; private final String _statGroup; /** probabalistic tunnel rejection due to a flood of requests - essentially unused */ public static final int TUNNEL_REJECT_PROBABALISTIC_REJECT = 10; /** tunnel rejection due to temporary cpu/job/tunnel overload - essentially unused */ public static final int TUNNEL_REJECT_TRANSIENT_OVERLOAD = 20; /** tunnel rejection due to excess bandwidth usage */ public static final int TUNNEL_REJECT_BANDWIDTH = 30; /** tunnel rejection due to system failure - essentially unused */ public static final int TUNNEL_REJECT_CRIT = 50; public TunnelHistory(RouterContext context, String statGroup) { _context = context; _log = context.logManager().getLog(TunnelHistory.class); _statGroup = statGroup; createRates(statGroup); } private void createRates(String statGroup) { _rejectRate = new RateStat("tunnelHistory.rejectRate", "How often does this peer reject a tunnel request?", statGroup, new long[] { 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[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l }); _rejectRate.setStatLog(_context.statManager().getStatLog()); _failRate.setStatLog(_context.statManager().getStatLog()); } /** total tunnels the peer has agreed to participate in */ public long getLifetimeAgreedTo() { return _lifetimeAgreedTo.get(); } /** total tunnels the peer has refused to participate in */ public long getLifetimeRejected() { return _lifetimeRejected.get(); } /** total tunnels the peer has agreed to participate in that were later marked as failed prematurely */ public long getLifetimeFailed() { return _lifetimeFailed.get(); } /** when the peer last agreed to participate in a tunnel */ public long getLastAgreedTo() { return _lastAgreedTo; } /** when the peer last refused to participate in a tunnel with level of critical */ public long getLastRejectedCritical() { return _lastRejectedCritical; } /** when the peer last refused to participate in a tunnel complaining of bandwidth overload */ public long getLastRejectedBandwidth() { return _lastRejectedBandwidth; } /** when the peer last refused to participate in a tunnel complaining of transient overload */ public long getLastRejectedTransient() { return _lastRejectedTransient; } /** when the peer last refused to participate in a tunnel probabalistically */ public long getLastRejectedProbabalistic() { return _lastRejectedProbabalistic; } /** when the last tunnel the peer participated in failed */ public long getLastFailed() { return _lastFailed; } public void incrementProcessed(int processedSuccessfully, int failedProcessing) { // old strict speed calculator } public void incrementAgreedTo() { _lifetimeAgreedTo.incrementAndGet(); _lastAgreedTo = _context.clock().now(); } /** * @param severity how much the peer doesnt want to participate in the * tunnel (large == more severe) */ public void incrementRejected(int severity) { _lifetimeRejected.incrementAndGet(); if (severity >= TUNNEL_REJECT_CRIT) { _lastRejectedCritical = _context.clock().now(); _rejectRate.addData(1); } else if (severity >= TUNNEL_REJECT_BANDWIDTH) { _lastRejectedBandwidth = _context.clock().now(); _rejectRate.addData(1); } else if (severity >= TUNNEL_REJECT_TRANSIENT_OVERLOAD) { _lastRejectedTransient = _context.clock().now(); // dont increment the reject rate in this case } else if (severity >= TUNNEL_REJECT_PROBABALISTIC_REJECT) { _lastRejectedProbabalistic = _context.clock().now(); // dont increment the reject rate in this case } } /** * Define this rate as the probability it really failed * @param pct = probability * 100 */ public void incrementFailed(int pct) { _lifetimeFailed.incrementAndGet(); _failRate.addData(pct); _lastFailed = _context.clock().now(); } /***** all unused public void setLifetimeAgreedTo(long num) { _lifetimeAgreedTo = num; } public void setLifetimeRejected(long num) { _lifetimeRejected = num; } public void setLifetimeFailed(long num) { _lifetimeFailed = num; } public void setLastAgreedTo(long when) { _lastAgreedTo = when; } public void setLastRejectedCritical(long when) { _lastRejectedCritical = when; } public void setLastRejectedBandwidth(long when) { _lastRejectedBandwidth = when; } public void setLastRejectedTransient(long when) { _lastRejectedTransient = when; } public void setLastRejectedProbabalistic(long when) { _lastRejectedProbabalistic = when; } public void setLastFailed(long when) { _lastFailed = when; } ******/ public RateStat getRejectionRate() { return _rejectRate; } public RateStat getFailedRate() { return _failRate; } public void coalesceStats() { if (_log.shouldLog(Log.DEBUG)) _log.debug("Coallescing stats"); _rejectRate.coalesceStats(); _failRate.coalesceStats(); } private final static String NL = System.getProperty("line.separator"); public void store(OutputStream out) throws IOException { StringBuilder buf = new StringBuilder(512); buf.append(NL); buf.append("#################").append(NL); buf.append("# Tunnel history").append(NL); buf.append("###").append(NL); addDate(buf, "lastAgreedTo", _lastAgreedTo, "When did the peer last agree to participate in a tunnel?"); addDate(buf, "lastFailed", _lastFailed, "When was the last time a tunnel that the peer agreed to participate failed?"); addDate(buf, "lastRejectedCritical", _lastRejectedCritical, "When was the last time the peer refused to participate in a tunnel (Critical response code)?"); addDate(buf, "lastRejectedBandwidth", _lastRejectedBandwidth, "When was the last time the peer refused to participate in a tunnel (Bandwidth response code)?"); addDate(buf, "lastRejectedTransient", _lastRejectedTransient, "When was the last time the peer refused to participate in a tunnel (Transient load response code)?"); addDate(buf, "lastRejectedProbabalistic", _lastRejectedProbabalistic, "When was the last time the peer refused to participate in a tunnel (Probabalistic response code)?"); add(buf, "lifetimeAgreedTo", _lifetimeAgreedTo.get(), "How many tunnels has the peer ever agreed to participate in?"); add(buf, "lifetimeFailed", _lifetimeFailed.get(), "How many tunnels has the peer ever agreed to participate in that failed prematurely?"); add(buf, "lifetimeRejected", _lifetimeRejected.get(), "How many tunnels has the peer ever refused to participate in?"); out.write(buf.toString().getBytes("UTF-8")); _rejectRate.store(out, "tunnelHistory.rejectRate"); _failRate.store(out, "tunnelHistory.failRate"); } private static void addDate(StringBuilder buf, String name, long val, String description) { String when = val > 0 ? (new Date(val)).toString() : "Never"; add(buf, name, val, description + ' ' + when); } private static void add(StringBuilder buf, String name, long val, String description) { buf.append("# ").append(name).append(NL).append("# ").append(description).append(NL); buf.append("tunnels.").append(name).append('=').append(val).append(NL).append(NL); } public void load(Properties props) { _lastAgreedTo = getLong(props, "tunnels.lastAgreedTo"); _lastFailed = getLong(props, "tunnels.lastFailed"); _lastRejectedCritical = getLong(props, "tunnels.lastRejectedCritical"); _lastRejectedBandwidth = getLong(props, "tunnels.lastRejectedBandwidth"); _lastRejectedTransient = getLong(props, "tunnels.lastRejectedTransient"); _lastRejectedProbabalistic = getLong(props, "tunnels.lastRejectedProbabalistic"); _lifetimeAgreedTo.set(getLong(props, "tunnels.lifetimeAgreedTo")); _lifetimeFailed.set(getLong(props, "tunnels.lifetimeFailed")); _lifetimeRejected.set(getLong(props, "tunnels.lifetimeRejected")); try { _rejectRate.load(props, "tunnelHistory.rejectRate", true); if (_log.shouldLog(Log.DEBUG)) _log.debug("Loading tunnelHistory.rejectRate"); _failRate.load(props, "tunnelHistory.failRate", true); if (_log.shouldLog(Log.DEBUG)) _log.debug("Loading tunnelHistory.failRate"); } catch (IllegalArgumentException iae) { _log.warn("TunnelHistory rates are corrupt, resetting", iae); createRates(_statGroup); } } private final static long getLong(Properties props, String key) { return ProfilePersistenceHelper.getLong(props, key); } }