package net.i2p.router;
import net.i2p.data.Hash;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.peermanager.TunnelHistory;
import net.i2p.stat.Rate;
import net.i2p.stat.RateAverages;
import net.i2p.stat.RateStat;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer;
import net.i2p.util.SystemVersion;
/**
* Simple throttle that basically stops accepting messages or nontrivial
* requests if the jobQueue lag is too large.
*
*/
public class RouterThrottleImpl implements RouterThrottle {
protected final RouterContext _context;
private final Log _log;
private volatile String _tunnelStatus;
/**
* arbitrary hard limit - if it's taking this long to get
* to a job, we're congested.
*
*/
private static final long JOB_LAG_LIMIT_NETWORK = 2*1000;
private static final long JOB_LAG_LIMIT_NETDB = 2*1000;
// TODO reduce
private static final long JOB_LAG_LIMIT_TUNNEL = 500;
public static final String PROP_MAX_TUNNELS = "router.maxParticipatingTunnels";
public static final int DEFAULT_MAX_TUNNELS = 10*1000;
private static final String PROP_MAX_PROCESSINGTIME = "router.defaultProcessingTimeThrottle";
private static final long DEFAULT_REJECT_STARTUP_TIME = 10*60*1000;
private static final String PROP_REJECT_STARTUP_TIME = "router.rejectStartupTime";
private static final int DEFAULT_MIN_THROTTLE_TUNNELS = SystemVersion.isAndroid() ? 100 :
SystemVersion.isARM() ? 500 : 1000;
/**
* TO BE FIXED - SEE COMMENTS BELOW
*/
private static final int DEFAULT_MAX_PROCESSINGTIME = 2250;
/** tunnel acceptance */
public static final int TUNNEL_ACCEPT = 0;
/** = TrivialPreprocessor.PREPROCESSED_SIZE */
private static final int PREPROCESSED_SIZE = 1024;
public RouterThrottleImpl(RouterContext context) {
_context = context;
_log = context.logManager().getLog(RouterThrottleImpl.class);
setTunnelStatus();
_context.simpleTimer2().addEvent(new ResetStatus(), 5*1000 + _context.getProperty(PROP_REJECT_STARTUP_TIME, DEFAULT_REJECT_STARTUP_TIME));
_context.statManager().createRateStat("router.throttleNetworkCause", "How lagged the jobQueue was when an I2NP was throttled", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
//_context.statManager().createRateStat("router.throttleNetDbCause", "How lagged the jobQueue was when a networkDb request was throttled", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("router.throttleTunnelCause", "How lagged the jobQueue was when a tunnel request was throttled", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("tunnel.bytesAllocatedAtAccept", "How many bytes had been 'allocated' for participating tunnels when we accepted a request?", "Tunnels", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("router.throttleTunnelProcessingTime1m", "How long it takes to process a message (1 minute average) when we throttle a tunnel?", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("router.throttleTunnelProcessingTime10m", "How long it takes to process a message (10 minute average) when we throttle a tunnel?", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000, 24*60*60*1000 });
_context.statManager().createRateStat("router.throttleTunnelMaxExceeded", "How many tunnels we are participating in when we refuse one due to excees?", "Throttle", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 });
_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 });
_context.statManager().createRateStat("router.throttleTunnelFailCount1m", "How many messages failed to be sent in the last 2 minutes when we throttle based on a spike in failures (period = 10 minute average failure count)?", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000});
//_context.statManager().createRateStat("router.throttleTunnelQueueOverload", "How many pending tunnel request messages have we received when we reject them due to overload (period = time to process each)?", "Throttle", new long[] { 60*1000, 10*60*1000, 60*60*1000});
}
/**
* Reset status from starting up to not-starting up,
* in case we don't get a tunnel request soon after the 20 minutes is up.
*
* @since 0.8.12
*/
private class ResetStatus implements SimpleTimer.TimedEvent {
public void timeReached() {
if (_tunnelStatus.equals(_x("Rejecting tunnels: Starting up")))
cancelShutdownStatus();
}
}
/**
* Should we accept any more data from the network for any sort of message,
* taking into account our current load, or should we simply slow down?
*
* FIXME only called by SSU Receiver, not NTCP!
* FIXME should put warning on the console
* FIXME or should we do this at all? We have Codel queues all over now...
*/
public boolean acceptNetworkMessage() {
//if (true) return true;
long lag = _context.jobQueue().getMaxLag();
if ( (lag > JOB_LAG_LIMIT_NETWORK) && (_context.router().getUptime() > 60*1000) ) {
if (_log.shouldLog(Log.WARN))
_log.warn("Throttling network reader, as the job lag is " + lag);
_context.statManager().addRateData("router.throttleNetworkCause", lag);
return false;
} else {
return true;
}
}
/** @deprecated unused, function moved to netdb */
@Deprecated
public boolean acceptNetDbLookupRequest(Hash key) {
long lag = _context.jobQueue().getMaxLag();
if (lag > JOB_LAG_LIMIT_NETDB) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Refusing netDb request, as the job lag is " + lag);
_context.statManager().addRateData("router.throttleNetDbCause", lag);
return false;
} else {
return true;
}
}
/**
* If we should send a reject, return a nonzero reject code.
* Anything that causes us to drop a request instead of rejecting it
* must go in BuildHandler.handleInboundRequest(), not here.
*
* @return 0 for accept or nonzero reject code
*/
public int acceptTunnelRequest() {
if (_context.router().gracefulShutdownInProgress()) {
if (_log.shouldLog(Log.WARN))
_log.warn("Refusing tunnel request since we are shutting down ASAP");
setShutdownStatus();
// Don't use CRIT because this tells everybody we are shutting down
return TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
// Don't use CRIT because we don't want peers to think we're failing
if (_context.router().getUptime() < DEFAULT_REJECT_STARTUP_TIME) {
setTunnelStatus(_x("Rejecting tunnels: Starting up"));
return TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
/**** Moved to BuildHandler
long lag = _context.jobQueue().getMaxLag();
if (lag > JOB_LAG_LIMIT_TUNNEL) {
if (_log.shouldLog(Log.WARN))
_log.warn("Refusing tunnel request, as the job lag is " + lag);
_context.statManager().addRateData("router.throttleTunnelCause", lag);
setTunnelStatus(_x("Rejecting tunnels: High job lag"));
return TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
****/
RateAverages ra = RateAverages.getTemp();
// TODO
// This stat is highly dependent on transport mix.
// For NTCP, it is queueing delay only, ~25ms
// For SSU it is queueing + ack time, ~1000 ms.
// (SSU acks may be delayed so it is much more than just RTT... and the delay may
// counterintuitively be more when there is low traffic)
// Change the stat or pick a better stat.
RateStat rs = _context.statManager().getRate("transport.sendProcessingTime");
Rate r = null;
if (rs != null)
r = rs.getRate(60*1000);
//Reject tunnels if the time to process messages and send them is too large. Too much time implies congestion.
if(r != null) {
r.computeAverages(ra,false);
int maxProcessingTime = _context.getProperty(PROP_MAX_PROCESSINGTIME, DEFAULT_MAX_PROCESSINGTIME);
//Set throttling if necessary
if((ra.getAverage() > maxProcessingTime*0.9
|| ra.getCurrent() > maxProcessingTime
|| ra.getLast() > maxProcessingTime)) {
if(_log.shouldLog(Log.WARN)) {
_log.warn("Refusing tunnel request due to sendProcessingTime " +
((int)ra.getCurrent()) + " / " +
((int)ra.getLast()) + " / " +
((int)ra.getAverage()) + " / " +
maxProcessingTime +
" current/last/avg/max ms");
}
setTunnelStatus(_x("Rejecting tunnels: High message delay"));
return TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
}
int numTunnels = _context.tunnelManager().getParticipatingCount();
int maxTunnels = _context.getProperty(PROP_MAX_TUNNELS, DEFAULT_MAX_TUNNELS);
if (numTunnels >= maxTunnels) {
if (_log.shouldLog(Log.WARN))
_log.warn("Refusing tunnel request since we are already participating in "
+ numTunnels + " (our max is " + maxTunnels + ")");
_context.statManager().addRateData("router.throttleTunnelMaxExceeded", numTunnels);
setTunnelStatus(_x("Rejecting tunnels: Limit reached"));
return TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
/*
* Throttle if we go above a minimum level of tunnels AND the maximum participating
* tunnels is default or lower.
*
* Lag based statistics use a moving average window (of for example 10 minutes), they are therefore
* sensitive to sudden rapid growth of load, which are not instantly detected by these metrics.
* Reduce tunnel growth if we are growing faster than the lag based metrics can detect reliably.
*/
if ((numTunnels > getMinThrottleTunnels()) && (DEFAULT_MAX_TUNNELS >= maxTunnels)) {
Rate avgTunnels = _context.statManager().getRate("tunnel.participatingTunnels").getRate(10*60*1000);
if (avgTunnels != null) {
double avg = avgTunnels.getAvgOrLifetimeAvg();
double tunnelGrowthFactor = getTunnelGrowthFactor();
int min = getMinThrottleTunnels();
if (avg < min)
avg = min;
// if the current tunnel count is higher than 1.3 * the average...
if ( (avg > 0) && (avg*tunnelGrowthFactor < numTunnels) ) {
// we're accelerating, lets try not to take on too much too fast
double probAccept = (avg*tunnelGrowthFactor) / numTunnels;
probAccept *= probAccept; // square the decelerator for tunnel counts
int v = _context.random().nextInt(100);
if (v < probAccept*100) {
// ok
if (_log.shouldLog(Log.INFO))
_log.info("Probabalistically accept tunnel request (p=" + probAccept
+ " v=" + v + " avg=" + avg + " current=" + numTunnels + ")");
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Probabalistically refusing tunnel request (avg=" + avg
+ " current=" + numTunnels + ")");
_context.statManager().addRateData("router.throttleTunnelProbTooFast", (long)(numTunnels-avg));
// hard to do {0} from here
//setTunnelStatus("Rejecting " + (100 - (int) probAccept*100) + "% of tunnels: High number of requests");
if (probAccept <= 0.5)
setTunnelStatus(_x("Rejecting most tunnels: High number of requests"));
else if (probAccept <= 0.9)
setTunnelStatus(_x("Accepting most tunnels"));
else
setTunnelStatus(_x("Accepting tunnels"));
return TunnelHistory.TUNNEL_REJECT_PROBABALISTIC_REJECT;
}
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Accepting tunnel request, since the tunnel count average is " + avg
+ " and we only have " + numTunnels + ")");
}
}
}
double tunnelTestTimeGrowthFactor = getTunnelTestTimeGrowthFactor();
Rate tunnelTestTime1m = _context.statManager().getRate("tunnel.testSuccessTime").getRate(1*60*1000);
Rate tunnelTestTime10m = _context.statManager().getRate("tunnel.testSuccessTime").getRate(10*60*1000);
if ( (tunnelTestTime1m != null) && (tunnelTestTime10m != null) && (tunnelTestTime1m.getLastEventCount() > 0) ) {
double avg1m = tunnelTestTime1m.getAverageValue();
double avg10m = tunnelTestTime10m.getAvgOrLifetimeAvg();
if (avg10m < 5000)
avg10m = 5000; // minimum before complaining
if ( (avg10m > 0) && (avg1m > avg10m * tunnelTestTimeGrowthFactor) ) {
double probAccept = (avg10m*tunnelTestTimeGrowthFactor)/avg1m;
probAccept = probAccept * probAccept; // square the decelerator for test times
int v = _context.random().nextInt(100);
if (v < probAccept*100) {
// ok
if (_log.shouldLog(Log.INFO))
_log.info("Probabalistically accept tunnel request (p=" + probAccept
+ " v=" + v + " test time avg 1m=" + avg1m + " 10m=" + avg10m + ")");
//} else if (false) {
// if (_log.shouldLog(Log.WARN))
// _log.warn("Probabalistically refusing tunnel request (test time avg 1m=" + avg1m
// + " 10m=" + avg10m + ")");
// _context.statManager().addRateData("router.throttleTunnelProbTestSlow", (long)(avg1m-avg10m), 0);
// setTunnelStatus("Rejecting " + ((int) probAccept*100) + "% of tunnels: High test time");
// return TunnelHistory.TUNNEL_REJECT_PROBABALISTIC_REJECT;
}
} else {
// not yet...
//if (_log.shouldLog(Log.INFO))
// _log.info("Accepting tunnel request, since 60m test time average is " + avg10m
// + " and past 1m only has " + avg1m + ")");
}
}
// ok, we're not hosed, but can we handle the bandwidth requirements
// of another tunnel?
rs = _context.statManager().getRate("tunnel.participatingMessageCountAvgPerTunnel");
r = null;
double messagesPerTunnel = DEFAULT_MESSAGES_PER_TUNNEL_ESTIMATE;
if (rs != null) {
r = rs.getRate(60*1000);
if (r != null)
messagesPerTunnel = r.computeAverages(ra, true).getAverage();
}
if (messagesPerTunnel < DEFAULT_MESSAGES_PER_TUNNEL_ESTIMATE)
messagesPerTunnel = DEFAULT_MESSAGES_PER_TUNNEL_ESTIMATE;
double bytesAllocated = messagesPerTunnel * numTunnels * PREPROCESSED_SIZE;
if (!allowTunnel(bytesAllocated, numTunnels)) {
_context.statManager().addRateData("router.throttleTunnelBandwidthExceeded", (long)bytesAllocated);
return TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
}
/***
int queuedRequests = _context.tunnelManager().getInboundBuildQueueSize();
int timePerRequest = 1000;
rs = _context.statManager().getRate("tunnel.decryptRequestTime");
if (rs != null) {
r = rs.getRate(60*1000);
if (r.getLastEventCount() > 0)
timePerRequest = (int)r.getAverageValue();
else
timePerRequest = (int)rs.getLifetimeAverageValue();
}
float pctFull = (queuedRequests * timePerRequest) / (4*1000f);
double pReject = Math.pow(pctFull, 16); //1 - ((1-pctFull) * (1-pctFull));
***/
// let it in because we drop overload- rejecting may be overkill,
// especially since we've done the cpu-heavy lifting to figure out
// whats up
/*
if ( (pctFull >= 1) || (pReject >= _context.random().nextFloat()) ) {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting a new tunnel request because we have too many pending requests (" + queuedRequests
+ " at " + timePerRequest + "ms each, %full = " + pctFull);
_context.statManager().addRateData("router.throttleTunnelQueueOverload", queuedRequests, timePerRequest);
return TunnelHistory.TUNNEL_REJECT_TRANSIENT_OVERLOAD;
}
*/
// ok, all is well, let 'er in
_context.statManager().addRateData("tunnel.bytesAllocatedAtAccept", (long)bytesAllocated, 60*10*1000);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Accepting a new tunnel request (now allocating " + bytesAllocated + " bytes across " + numTunnels
// + " tunnels with lag of " + lag + ")");
return TUNNEL_ACCEPT;
}
private static final int DEFAULT_MESSAGES_PER_TUNNEL_ESTIMATE = 40; // .067KBps
/** also limited to 90% - see below */
private static final int MIN_AVAILABLE_BPS = 4*1024; // always leave at least 4KBps free when allowing
private static final String LIMIT_STR = _x("Rejecting tunnels: Bandwidth limit");
/**
* with bytesAllocated already accounted for across the numTunnels existing
* tunnels we have agreed to, can we handle another tunnel with our existing
* bandwidth?
*
*/
private boolean allowTunnel(double bytesAllocated, int numTunnels) {
int maxKBpsIn = _context.bandwidthLimiter().getInboundKBytesPerSecond();
int maxKBpsOut = _context.bandwidthLimiter().getOutboundKBytesPerSecond();
int maxKBps = Math.min(maxKBpsIn, maxKBpsOut);
int usedIn = Math.min(_context.router().get1sRateIn(), _context.router().get15sRateIn());
int usedOut = Math.min(_context.router().get1sRate(true), _context.router().get15sRate(true));
int used = Math.max(usedIn, usedOut);
int used1mIn = _context.router().get1mRateIn();
int used1mOut = _context.router().get1mRate(true);
// Check the inbound and outbound total bw available (separately)
// We block all tunnels when share bw is over (max * 0.9) - 4KB
// This gives reasonable growth room for existing tunnels on both low and high
// bandwidth routers. We want to be rejecting tunnels more aggressively than
// dropping packets with WRED
int availBps = Math.min((maxKBpsIn*1024*9/10) - usedIn, (maxKBpsOut*1024*9/10) - usedOut);
if (availBps < MIN_AVAILABLE_BPS) {
if (_log.shouldLog(Log.WARN)) _log.warn("Reject, avail (" + availBps + ") less than min");
setTunnelStatus(LIMIT_STR);
return false;
}
// Now compute the share bw available, using
// the bytes-allocated estimate for the participating tunnels
// (if lower than the total bw, which it should be),
// since some of the total used bandwidth may be for local clients
double share = _context.router().getSharePercentage();
used = Math.min(used, (int) (bytesAllocated / (10*60)));
availBps = Math.min(availBps, (int)(((maxKBps*1024)*share) - used));
// Write stats before making decisions
_context.statManager().addRateData("router.throttleTunnelBytesUsed", used, maxKBps);
_context.statManager().addRateData("router.throttleTunnelBytesAllowed", availBps, (long)bytesAllocated);
// Now see if 1m rates are too high
long overage = Math.max(used1mIn - (maxKBpsIn*1024), used1mOut - (maxKBpsOut*1024));
if ( (overage > 0) &&
((overage/(maxKBps*1024f)) > _context.random().nextFloat()) ) {
if (_log.shouldLog(Log.WARN)) _log.warn("Reject tunnel, 1m rate (" + overage + " over) indicates overload.");
setTunnelStatus(LIMIT_STR);
return false;
}
// limit at 90% - 4KBps (see above)
float maxBps = (maxKBps * 1024f * 0.9f) - MIN_AVAILABLE_BPS;
float pctFull = (maxBps - availBps) / (maxBps);
double probReject = Math.pow(pctFull, 16); // steep curve
double rand = _context.random().nextFloat();
boolean reject = rand <= probReject;
if (reject && _log.shouldLog(Log.WARN))
_log.warn("Reject avail/maxK/used " + availBps + "/" + maxKBps + "/"
+ used + " pReject = " + probReject + " pFull = " + pctFull + " numTunnels = " + numTunnels
+ " rand = " + rand + " est = " + bytesAllocated);
else if (_log.shouldLog(Log.DEBUG))
_log.debug("Accept avail/maxK/used " + availBps + "/" + maxKBps + "/"
+ used + " pReject = " + probReject + " pFull = " + pctFull + " numTunnels = " + numTunnels
+ " rand = " + rand + " est = " + bytesAllocated);
if (probReject >= 0.9)
setTunnelStatus(LIMIT_STR);
else if (probReject >= 0.5)
// hard to do {0} from here
//setTunnelStatus("Rejecting " + ((int)(100.0*probReject)) + "% of tunnels: Bandwidth limit");
setTunnelStatus(_x("Rejecting most tunnels: Bandwidth limit"));
else if(probReject >= 0.1)
// hard to do {0} from here
//setTunnelStatus("Accepting " + (100-(int)(100.0*probReject)) + "% of tunnels");
setTunnelStatus(_x("Accepting most tunnels"));
else
setTunnelStatus(_x("Accepting tunnels"));
return !reject;
/*
if (availBps <= 8*1024) {
// lets be more conservative for people near their limit and assume 1KBps per tunnel
boolean rv = ( (numTunnels + 1)*1024 < availBps);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Nearly full router (" + availBps + ") with " + numTunnels + " tunnels, allow a new request? " + rv);
return rv;
}
*/
/***
double growthFactor = ((double)(numTunnels+1))/(double)numTunnels;
double toAllocate = (numTunnels > 0 ? bytesAllocated * growthFactor : 0);
double allocatedBps = toAllocate / (10 * 60);
double pctFull = allocatedBps / availBps;
if ( (pctFull < 1.0) && (pctFull >= 0.0) ) { // (_context.random().nextInt(100) > 100 * pctFull) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Allowing the tunnel w/ " + pctFull + " of our " + availBps
+ "Bps/" + allocatedBps + "KBps allocated through " + numTunnels + " tunnels");
return true;
} else {
double probAllow = availBps / (allocatedBps + availBps);
boolean allow = (availBps > MIN_AVAILABLE_BPS) && (_context.random().nextFloat() <= probAllow);
if (allow) {
if (_log.shouldLog(Log.INFO))
_log.info("Probabalistically allowing the tunnel w/ " + (pctFull*100d) + "% of our " + availBps
+ "Bps allowed (" + toAllocate + "bytes / " + allocatedBps
+ "Bps) through " + numTunnels + " tunnels");
return true;
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("Rejecting the tunnel w/ " + (pctFull*100d) + "% of our " + availBps
+ "Bps allowed (" + toAllocate + "bytes / " + allocatedBps
+ "Bps) through " + numTunnels + " tunnels");
return false;
}
}
***/
}
/** dont ever probabalistically throttle tunnels if we have less than this many */
private int getMinThrottleTunnels() {
return _context.getProperty("router.minThrottleTunnels", DEFAULT_MIN_THROTTLE_TUNNELS);
}
private double getTunnelGrowthFactor() {
try {
return Double.parseDouble(_context.getProperty("router.tunnelGrowthFactor", "1.3"));
} catch (NumberFormatException nfe) {
return 1.3;
}
}
private double getTunnelTestTimeGrowthFactor() {
try {
return Double.parseDouble(_context.getProperty("router.tunnelTestTimeGrowthFactor", "1.3"));
} catch (NumberFormatException nfe) {
return 1.3;
}
}
public long getMessageDelay() {
RateStat rs = _context.statManager().getRate("transport.sendProcessingTime");
if (rs == null)
return 0;
Rate delayRate = rs.getRate(60*1000);
return (long)delayRate.getAverageValue();
}
public long getTunnelLag() {
Rate lagRate = _context.statManager().getRate("tunnel.testSuccessTime").getRate(10*60*1000);
return (long)lagRate.getAverageValue();
}
public double getInboundRateDelta() {
RateStat receiveRate = _context.statManager().getRate("transport.sendMessageSize");
if (receiveRate == null)
return 0;
double nowBps = getBps(receiveRate.getRate(60*1000));
double fiveMinBps = getBps(receiveRate.getRate(5*60*1000));
double hourBps = getBps(receiveRate.getRate(60*60*1000));
double dailyBps = getBps(receiveRate.getRate(24*60*60*1000));
if (nowBps < 0) return 0;
if (dailyBps > 0) return nowBps - dailyBps;
if (hourBps > 0) return nowBps - hourBps;
if (fiveMinBps > 0) return nowBps - fiveMinBps;
return 0;
}
private static double getBps(Rate rate) {
if (rate == null) return -1;
double bytes = rate.getLastTotalValue();
return (bytes*1000.0d)/rate.getPeriod();
}
public String getTunnelStatus() {
return _tunnelStatus;
}
private void setTunnelStatus() {
// NPE, too early
// if (_context.router().getRouterInfo().getBandwidthTier().equals("K"))
// setTunnelStatus("Not expecting tunnel requests: Advertised bandwidth too low");
// else
setTunnelStatus(_x("Rejecting tunnels: Starting up"));
}
/** @since 0.8.12 */
public void setShutdownStatus() {
setTunnelStatus(_x("Rejecting tunnels: Shutting down"));
}
/** @since 0.8.12 */
public void cancelShutdownStatus() {
// try hard to guess the state, before we actually get a request
int maxTunnels = _context.getProperty(PROP_MAX_TUNNELS, DEFAULT_MAX_TUNNELS);
RouterInfo ri = _context.router().getRouterInfo();
if (maxTunnels > 0 &&
!_context.router().isHidden() &&
ri != null && !ri.getBandwidthTier().equals("K")) {
setTunnelStatus(_x("Accepting tunnels"));
} else {
setTunnelStatus(_x("Rejecting tunnels"));
}
}
public void setTunnelStatus(String msg) {
_tunnelStatus = msg;
}
/**
* Mark a string for extraction by xgettext and translation.
* Use this only in static initializers.
* It does not translate!
* @return s
*/
private static final String _x(String s) {
return s;
}
}