Author: toad
Date: 2008-04-18 13:43:11 +0000 (Fri, 18 Apr 2008)
New Revision: 19391
Modified:
trunk/freenet/src/freenet/node/Node.java
trunk/freenet/src/freenet/support/DoubleTokenBucket.java
trunk/freenet/src/freenet/support/TokenBucket.java
Log:
Rewrite DoubleTokenBucket as per bug #2279. Hopefully bandwidth limiting will
work a bit better now.
Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java 2008-04-18 12:54:36 UTC (rev
19390)
+++ trunk/freenet/src/freenet/node/Node.java 2008-04-18 13:43:11 UTC (rev
19391)
@@ -885,7 +885,7 @@
synchronized(Node.this) {
outputBandwidthLimit =
obwLimit;
}
-
outputThrottle.changeNanosAndBucketSizes((1000L * 1000L * 1000L) / obwLimit,
obwLimit/2, (obwLimit * 2) / 5);
+
outputThrottle.changeNanosAndBucketSize((1000L * 1000L * 1000L) / obwLimit,
obwLimit/2);
nodeStats.setOutputLimit(obwLimit);
}
});
@@ -898,7 +898,7 @@
// Add them at a rate determined by the obwLimit.
// Maximum forced bytes 80%, in other words, 20% of the
bandwidth is reserved for
// block transfers, so we will use that 20% for block transfers
even if more than 80% of the limit is used for non-limited data (resends etc).
- outputThrottle = new DoubleTokenBucket(obwLimit/2,
(1000L*1000L*1000L) / obwLimit, obwLimit, (obwLimit * 2) / 5);
+ outputThrottle = new DoubleTokenBucket(obwLimit/2,
(1000L*1000L*1000L) / obwLimit, obwLimit, 0.8);
nodeConfig.register("inputBandwidthLimit", "-1", sortOrder++,
false, true, "Node.inBWLimit", "Node.inBWLimitLong", new IntCallback() {
public int get() {
Modified: trunk/freenet/src/freenet/support/DoubleTokenBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/DoubleTokenBucket.java 2008-04-18
12:54:36 UTC (rev 19390)
+++ trunk/freenet/src/freenet/support/DoubleTokenBucket.java 2008-04-18
13:43:11 UTC (rev 19391)
@@ -1,101 +1,50 @@
package freenet.support;
/**
- * A TokenBucket variant which provides the following constraint:
- * Forced token grabs may not use more than M tokens out of the total N.
- *
- * In other words:
- * A classic token bucket keeps a counter, "current", which must not exceed
the max, to
- * which we add one token every few millis.
- *
- * We also keep another counter, unblockableCount, with a separate maximum,
- * unblockableMax. Whenever a token is added to current by the clock, one is
removed
- * from unblockableCount (but only if it is >0). Whenever an unblockable
request to
- * remove tokens comes in, after we update unblockableCount, we calculate how
many
- * tokens we can add to unblockableCount without exceeding the maximum, and
only allow
- * that many to be removed from counter.
+ * A TokenBucket where forceGrab() may only use up to some proportion of the
total limit. Beyond that,
+ * we ignore it. So the last X% may be used by blocking grabs, even if the
forceGrab() traffic is over
+ * the limit. This is implemented by using a secondary TokenBucket to track
what is allowed.
*/
public class DoubleTokenBucket extends TokenBucket {
- private long maxForced;
- private long curForced;
private static boolean logMINOR;
+ private final TokenBucket grabbedBytesLimiter;
+ private double forceGrabLimit;
- /**
- * Create a DoubleTokenBucket.
- * @param max The maximum size of the bucket, in tokens.
- * @param nanosPerTick The number of nanoseconds between ticks.
- *
- */
- public DoubleTokenBucket(long max, long nanosPerTick, long
initialValue, long maxForced) {
+ public DoubleTokenBucket(long max, long nanosPerTick, long
initialValue, double forceGrabLimit) {
super(max, nanosPerTick, initialValue);
+ if(forceGrabLimit > 1.0) throw new IllegalArgumentException();
logMINOR = Logger.shouldLog(Logger.MINOR, this);
- if(logMINOR)
- Logger.minor(this, "Max: "+max+" nanosPerTick:
"+nanosPerTick+" initialValue: "+initialValue+" maxForced: "+maxForced);
- this.maxForced = maxForced;
- this.curForced = 0;
+ grabbedBytesLimiter = new TokenBucket((long)(max *
forceGrabLimit), (long)(nanosPerTick * forceGrabLimit), (long)(initialValue *
forceGrabLimit));
}
- // instantGrab is unchanged.
-
- // Major changes to forceGrab ! This is where it happens.
-
public synchronized void forceGrab(long tokens) {
logMINOR = Logger.shouldLog(Logger.MINOR, this);
- addTokens();
- if(tokens <= 0) {
- Logger.error(this, "forceGrab("+tokens+") - negative
value!!", new Exception("error"));
- return;
+ long maxTokens = grabbedBytesLimiter.partialInstantGrab(tokens);
+ if(maxTokens < tokens) {
+ if(logMINOR) Logger.minor(this, "Limiting forceGrab of
"+tokens+" to "+maxTokens);
}
- long thisMax = maxForced - curForced;
- if(tokens > thisMax) {
- if(logMINOR) Logger.minor(this, "Limiting force-grab to
"+thisMax+" tokens was "+tokens);
- tokens = thisMax;
- }
- curForced += tokens;
- current -= tokens;
- if(curForced > maxForced) {
- curForced = maxForced;
- }
- if(logMINOR) Logger.minor(this, "Force-Grabbed "+tokens+"
current="+current+" forced="+curForced);
+ super.forceGrab(maxTokens);
}
-
- // blockingGrab is unchanged
-
- public synchronized void changeSizeOfBuckets(long newMax, long
newMaxForced) {
- changeBucketSize(newMax);
- this.maxForced = newMaxForced;
- if(curForced > maxForced) curForced = maxForced;
+
+ /**
+ * Change the number of nanos per tick.
+ * @param nanosPerTick The new number of nanos per tick.
+ */
+ public synchronized void changeNanosPerTick(long nanosPerTick) {
+ super.changeNanosPerTick(nanosPerTick);
+ grabbedBytesLimiter.changeNanosPerTick((long)(nanosPerTick *
forceGrabLimit));
}
- public synchronized void changeNanosAndBucketSizes(long nanos, long
newMax, long newMaxForced) {
- // FIXME maybe should be combined
- changeSizeOfBuckets(newMax, newMaxForced);
- changeNanosPerTick(nanos);
+ public synchronized void changeBucketSize(long newMax) {
+ super.changeBucketSize(newMax);
+ grabbedBytesLimiter.changeBucketSize((long)(newMax *
forceGrabLimit));
}
- public synchronized void addTokensNoClip() {
- long add = tokensToAdd();
- current += add;
- curForced -= add;
- if(curForced < 0) curForced = 0;
- timeLastTick += add * nanosPerTick;
- if(logMINOR) Logger.minor(this, "Added "+add+" tokens
current="+current+" forced="+curForced);
+ public synchronized void changeNanosAndBucketSize(long nanosPerTick,
long newMax) {
+ super.changeNanosAndBucketSize(nanosPerTick, newMax);
+
grabbedBytesLimiter.changeNanosAndBucketSize((long)(nanosPerTick *
forceGrabLimit),
+ (long)(newMax * forceGrabLimit));
}
-
- public synchronized void addTokens() {
- if(logMINOR) Logger.minor(this, "current="+current+"
forced="+curForced);
- addTokensNoClip();
- if(curForced > maxForced) curForced = maxForced;
- if(current > max) current = max;
- }
- public synchronized long getSize() {
- return max;
- }
-
- protected synchronized long offset() {
- return curForced;
- }
-
}
Modified: trunk/freenet/src/freenet/support/TokenBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/TokenBucket.java 2008-04-18 12:54:36 UTC
(rev 19390)
+++ trunk/freenet/src/freenet/support/TokenBucket.java 2008-04-18 13:43:11 UTC
(rev 19391)
@@ -54,6 +54,28 @@
}
/**
+ * Either grab a bunch of tokens, or don't. Never block.
+ * @param tokens The number of tokens to grab.
+ * @return True if we could acquire the tokens.
+ */
+ public synchronized long partialInstantGrab(long tokens) {
+ if(logMINOR)
+ Logger.minor(this, "instant grab: "+tokens+"
current="+current+" max="+max);
+ addTokens();
+ if(logMINOR)
+ Logger.minor(this, "instant grab: "+tokens+"
current="+current+" max="+max);
+ if(current > tokens) {
+ current -= tokens;
+ if(current > max) current = max;
+ return tokens;
+ } else {
+ tokens = current;
+ current = 0;
+ return tokens;
+ }
+ }
+
+ /**
* Remove tokens, without blocking, even if it causes the balance to go
negative.
* @param tokens The number of tokens to remove.
*/