From a8ecd32b45f5e68ac3be2a5c95f667f1c40146f4 Mon Sep 17 00:00:00 2001
From: jrandom <jrandom>
Date: Sat, 17 Sep 2005 23:01:44 +0000
Subject: [PATCH] 2005-09-17  jrandom     * Updated the bandwidth limiter to
 use two tiers of bandwidth - our normal       steady state rate, plus a new
 limit on how fast we transfer when       bursting.  This is different from
 the old "burst as fast as possible       until we're out of tokens" policy,
 and should help those with congested       networks.  See /config.jsp to
 manage this rate.     * Bugfixes in Syndie to handle missing cache files (no
 data was lost, the       old posts just didn't show up).     * Log properly
 in EepPost

---
 .../net/i2p/router/web/ConfigNetHandler.java  |  24 ++-
 .../net/i2p/router/web/ConfigNetHelper.java   |  20 +-
 apps/routerconsole/jsp/config.jsp             |   9 +-
 history.txt                                   |   7 +-
 .../src/net/i2p/router/RouterVersion.java     |   4 +-
 .../transport/FIFOBandwidthLimiter.java       | 174 +++++++++++++-----
 .../transport/FIFOBandwidthRefiller.java      | 141 ++++++++++----
 7 files changed, 287 insertions(+), 92 deletions(-)

diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
index 7c0518a92a..c3641d40f9 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHandler.java
@@ -34,8 +34,10 @@ public class ConfigNetHandler extends FormHandler {
     private String _tcpPort;
     private String _udpPort;
     private String _inboundRate;
+    private String _inboundBurstRate;
     private String _inboundBurst;
     private String _outboundRate;
+    private String _outboundBurstRate;
     private String _outboundBurst;
     private String _reseedFrom;
     private String _sharePct;
@@ -73,12 +75,18 @@ public class ConfigNetHandler extends FormHandler {
     public void setInboundrate(String rate) { 
         _inboundRate = (rate != null ? rate.trim() : null); 
     }
+    public void setInboundburstrate(String rate) { 
+        _inboundBurstRate = (rate != null ? rate.trim() : null); 
+    }
     public void setInboundburstfactor(String factor) { 
         _inboundBurst = (factor != null ? factor.trim() : null); 
     }
     public void setOutboundrate(String rate) { 
         _outboundRate = (rate != null ? rate.trim() : null); 
     }
+    public void setOutboundburstrate(String rate) { 
+        _outboundBurstRate = (rate != null ? rate.trim() : null); 
+    }
     public void setOutboundburstfactor(String factor) { 
         _outboundBurst = (factor != null ? factor.trim() : null); 
     }
@@ -293,14 +301,22 @@ public class ConfigNetHandler extends FormHandler {
             _context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS, _outboundRate);
             updated = true;
         }
+        if ( (_inboundBurstRate != null) && (_inboundBurstRate.length() > 0) ) {
+            _context.router().setConfigSetting(ConfigNetHelper.PROP_INBOUND_BURST_KBPS, _inboundBurstRate);
+            updated = true;
+        }
+        if ( (_outboundBurstRate != null) && (_outboundBurstRate.length() > 0) ) {
+            _context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_BURST_KBPS, _outboundBurstRate);
+            updated = true;
+        }
         
-        String inRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_INBOUND_KBPS);
+        String inBurstRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_INBOUND_BURST_KBPS);
         
         if (_inboundBurst != null) {
             int rateKBps = 0;
             int burstSeconds = 0;
             try {
-                rateKBps = Integer.parseInt(inRate);
+                rateKBps = Integer.parseInt(inBurstRate);
                 burstSeconds = Integer.parseInt(_inboundBurst);
             } catch (NumberFormatException nfe) {
                 // ignore
@@ -312,13 +328,13 @@ public class ConfigNetHandler extends FormHandler {
             }
         }
         
-        String outRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS);
+        String outBurstRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_OUTBOUND_BURST_KBPS);
         
         if (_outboundBurst != null) {
             int rateKBps = 0;
             int burstSeconds = 0;
             try {
-                rateKBps = Integer.parseInt(outRate);
+                rateKBps = Integer.parseInt(outBurstRate);
                 burstSeconds = Integer.parseInt(_outboundBurst);
             } catch (NumberFormatException nfe) {
                 // ignore
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
index c5ebb16cf9..562d07b3a9 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigNetHelper.java
@@ -84,6 +84,8 @@ public class ConfigNetHelper {
     
     public static final String PROP_INBOUND_KBPS = "i2np.bandwidth.inboundKBytesPerSecond";
     public static final String PROP_OUTBOUND_KBPS = "i2np.bandwidth.outboundKBytesPerSecond";
+    public static final String PROP_INBOUND_BURST_KBPS = "i2np.bandwidth.inboundBurstKBytesPerSecond";
+    public static final String PROP_OUTBOUND_BURST_KBPS = "i2np.bandwidth.outboundBurstKBytesPerSecond";
     public static final String PROP_INBOUND_BURST = "i2np.bandwidth.inboundBurstKBytes";
     public static final String PROP_OUTBOUND_BURST = "i2np.bandwidth.outboundBurstKBytes";
     public static final String PROP_SHARE_PERCENTAGE = "router.sharePercentage";
@@ -94,14 +96,28 @@ public class ConfigNetHelper {
         if (rate != null)
             return rate;
         else
-            return "-1";
+            return "16";
     }
     public String getOutboundRate() {
         String rate = _context.getProperty(PROP_OUTBOUND_KBPS);
         if (rate != null)
             return rate;
         else
-            return "-1";
+            return "16";
+    }
+    public String getInboundBurstRate() {
+        String rate = _context.getProperty(PROP_INBOUND_BURST_KBPS);
+        if (rate != null)
+            return rate;
+        else
+            return "32";
+    }
+    public String getOutboundBurstRate() {
+        String rate = _context.getProperty(PROP_OUTBOUND_BURST_KBPS);
+        if (rate != null)
+            return rate;
+        else
+            return "32";
     }
     public String getInboundBurstFactorBox() {
         String rate = _context.getProperty(PROP_INBOUND_KBPS);
diff --git a/apps/routerconsole/jsp/config.jsp b/apps/routerconsole/jsp/config.jsp
index 11b98ca410..bf214eef2d 100644
--- a/apps/routerconsole/jsp/config.jsp
+++ b/apps/routerconsole/jsp/config.jsp
@@ -39,14 +39,17 @@
  
  <b>Bandwidth limiter</b><br />
  Inbound rate: 
-    <input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBytes per second
+    <input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBps
  bursting up to 
+    <input name="inboundburstrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundBurstRate" />" /> KBps for
     <jsp:getProperty name="nethelper" property="inboundBurstFactorBox" /><br />
  Outbound rate:
-    <input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBytes per second
+    <input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBps
  bursting up to 
+    <input name="outboundburstrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundBurstRate" />" /> KBps for
   <jsp:getProperty name="nethelper" property="outboundBurstFactorBox" /><br />
- <i>A negative rate means a default limit of 16KBytes per second.</i><br />
+ <i>KBps = kilibytes per second = 1024 bytes per second.<br />
+    A negative rate means a default limit of 16KBytes per second.</i><br />
  Bandwidth share percentage:
    <jsp:getProperty name="nethelper" property="sharePercentageBox" /><br />
  Sharing a higher percentage will improve your anonymity and help the network
diff --git a/history.txt b/history.txt
index d24fb36f6e..2d3b79364d 100644
--- a/history.txt
+++ b/history.txt
@@ -1,6 +1,11 @@
-$Id: history.txt,v 1.254 2005/09/17 02:31:50 jrandom Exp $
+$Id: history.txt,v 1.255 2005/09/17 15:08:26 jrandom Exp $
 
 2005-09-17  jrandom
+    * Updated the bandwidth limiter to use two tiers of bandwidth - our normal
+      steady state rate, plus a new limit on how fast we transfer when 
+      bursting.  This is different from the old "burst as fast as possible 
+      until we're out of tokens" policy, and should help those with congested
+      networks.  See /config.jsp to manage this rate.
     * Bugfixes in Syndie to handle missing cache files (no data was lost, the
       old posts just didn't show up).
     * Log properly in EepPost
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 60869ef0e1..fe4b905864 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.238 $ $Date: 2005/09/16 13:28:26 $";
+    public final static String ID = "$Revision: 1.239 $ $Date: 2005/09/16 16:24:43 $";
     public final static String VERSION = "0.6.0.5";
-    public final static long BUILD = 13;
+    public final static long BUILD = 14;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
         System.out.println("Router ID: " + RouterVersion.ID);
diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java
index 0ed8f3e9df..df71d27492 100644
--- a/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java
+++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthLimiter.java
@@ -14,16 +14,34 @@ public class FIFOBandwidthLimiter {
     private I2PAppContext _context;
     private List _pendingInboundRequests;
     private List _pendingOutboundRequests;
-    private volatile int _availableInboundBytes;
-    private volatile int _availableOutboundBytes;
+    /** how many bytes we can consume for inbound transmission immediately */
+    private volatile int _availableInbound;
+    /** how many bytes we can consume for outbound transmission immediately */
+    private volatile int _availableOutbound;
+    /** how many bytes we can queue up for bursting */
+    private volatile int _unavailableInboundBurst;
+    /** how many bytes we can queue up for bursting */
+    private volatile int _unavailableOutboundBurst;
+    /** how large _unavailableInbound can get */
+    private int _maxInboundBurst;
+    /** how large _unavailableInbound can get */
+    private int _maxOutboundBurst;
+    /** how large _availableInbound can get - aka our inbound rate duringa burst */
+    private int _maxInbound;
+    /** how large _availableOutbound can get - aka our outbound rate during a burst */
+    private int _maxOutbound;
+    /** shortcut of whether our outbound rate is unlimited */
     private boolean _outboundUnlimited;
+    /** shortcut of whether our inbound rate is unlimited */
     private boolean _inboundUnlimited;
+    /** lifetime counter of bytes received */
     private volatile long _totalAllocatedInboundBytes;
+    /** lifetime counter of bytes sent */
     private volatile long _totalAllocatedOutboundBytes;
+    /** lifetime counter of tokens available for use but exceeded our maxInboundBurst size */
     private volatile long _totalWastedInboundBytes;
+    /** lifetime counter of tokens available for use but exceeded our maxOutboundBurst size */
     private volatile long _totalWastedOutboundBytes;
-    private int _maxInboundBytes;
-    private int _maxOutboundBytes;
     private FIFOBandwidthRefiller _refiller;
     
     private long _lastTotalSent;
@@ -65,16 +83,16 @@ public class FIFOBandwidthLimiter {
         t.start();
     }
 
-    public long getAvailableInboundBytes() { return _availableInboundBytes; }
-    public long getAvailableOutboundBytes() { return _availableOutboundBytes; }
+    //public long getAvailableInboundBytes() { return _availableInboundBytes; }
+    //public long getAvailableOutboundBytes() { return _availableOutboundBytes; }
     public long getTotalAllocatedInboundBytes() { return _totalAllocatedInboundBytes; }
     public long getTotalAllocatedOutboundBytes() { return _totalAllocatedOutboundBytes; }
     public long getTotalWastedInboundBytes() { return _totalWastedInboundBytes; }
     public long getTotalWastedOutboundBytes() { return _totalWastedOutboundBytes; }
-    public long getMaxInboundBytes() { return _maxInboundBytes; }
-    public void setMaxInboundBytes(int numBytes) { _maxInboundBytes = numBytes; }
-    public long getMaxOutboundBytes() { return _maxOutboundBytes; }
-    public void setMaxOutboundBytes(int numBytes) { _maxOutboundBytes = numBytes; }
+    //public long getMaxInboundBytes() { return _maxInboundBytes; }
+    //public void setMaxInboundBytes(int numBytes) { _maxInboundBytes = numBytes; }
+    //public long getMaxOutboundBytes() { return _maxOutboundBytes; }
+    //public void setMaxOutboundBytes(int numBytes) { _maxOutboundBytes = numBytes; }
     public boolean getInboundUnlimited() { return _inboundUnlimited; }
     public void setInboundUnlimited(boolean isUnlimited) { _inboundUnlimited = isUnlimited; }
     public boolean getOutboundUnlimited() { return _outboundUnlimited; }
@@ -83,8 +101,14 @@ public class FIFOBandwidthLimiter {
     public void reinitialize() {
         _pendingInboundRequests.clear();
         _pendingOutboundRequests.clear();
-        _availableInboundBytes = 0;
-        _availableOutboundBytes = 0;
+        _availableInbound = 0;
+        _availableOutbound = 0;
+        _maxInbound = 0;
+        _maxOutbound = 0;
+        _maxInboundBurst = 0;
+        _maxOutboundBurst = 0;
+        _unavailableInboundBurst = 0;
+        _unavailableOutboundBurst = 0;
         _inboundUnlimited = false;
         _outboundUnlimited = false;
         _refiller.reinitialize();
@@ -133,25 +157,94 @@ public class FIFOBandwidthLimiter {
         return req;
     }
     
+    void setInboundBurstKBps(int kbytesPerSecond) {
+        _maxInbound = kbytesPerSecond * 1024;
+    }
+    void setOutboundBurstKBps(int kbytesPerSecond) {
+        _maxOutbound = kbytesPerSecond * 1024;
+    }
+    int getInboundBurstBytes() { return _maxInboundBurst; }
+    int getOutboundBurstBytes() { return _maxOutboundBurst; }
+    void setInboundBurstBytes(int bytes) { _maxInboundBurst = bytes; }
+    void setOutboundBurstBytes(int bytes) { _maxOutboundBurst = bytes; }
+    
+    StringBuffer getStatus() {
+        StringBuffer rv = new StringBuffer(64);
+        rv.append("Available: ").append(_availableInbound).append('/').append(_availableOutbound).append(' ');
+        rv.append("Max: ").append(_maxInbound).append('/').append(_maxOutbound).append(' ');
+        rv.append("Burst: ").append(_unavailableInboundBurst).append('/').append(_unavailableOutboundBurst).append(' ');
+        rv.append("Burst max: ").append(_maxInboundBurst).append('/').append(_maxOutboundBurst).append(' ');
+        return rv;
+    }
+    
     /**
      * More bytes are available - add them to the queue and satisfy any requests
      * we can
      */
     final void refillBandwidthQueues(long bytesInbound, long bytesOutbound) {
         if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Refilling the queues with " + bytesInbound + "/" + bytesOutbound + ", available " + 
-                       _availableInboundBytes + '/' + _availableOutboundBytes + ", max " + 
-                       _maxInboundBytes + '/' + _maxOutboundBytes);
-        _availableInboundBytes += bytesInbound;
-        _availableOutboundBytes += bytesOutbound;
-        if (_availableInboundBytes > _maxInboundBytes) {
-            _totalWastedInboundBytes += (_availableInboundBytes - _maxInboundBytes);
-            _availableInboundBytes = _maxInboundBytes;
+            _log.debug("Refilling the queues with " + bytesInbound + "/" + bytesOutbound + ": " + getStatus().toString());
+        _availableInbound += bytesInbound;
+        _availableOutbound += bytesOutbound;
+        
+        if (_availableInbound > _maxInbound) {
+            if (_log.shouldLog(Log.DEBUG))
+                _log.debug("available inbound (" + _availableInbound + ") exceeds our inbound burst (" + _maxInbound + "), so no supplement");
+            _unavailableInboundBurst += _availableInbound - _maxInbound;
+            _availableInbound = _maxInbound;
+            if (_unavailableInboundBurst > _maxInboundBurst) {
+                _totalWastedInboundBytes += _unavailableInboundBurst - _maxInboundBurst;
+                _unavailableInboundBurst = _maxInboundBurst;
+            }
+        } else {
+            // try to pull in up to 1/10th of the max inbound rate (aka burst rate), since 
+            // we refill every 100ms
+            int want = _maxInbound/10;
+            if (want > (_maxInbound - _availableInbound))
+                want = _maxInbound - _availableInbound;
+            if (_log.shouldLog(Log.DEBUG))
+                _log.debug("want to pull " + want + " from the inbound burst (" + _unavailableInboundBurst + ") to supplement " + _availableInbound + " (max: " + _maxInbound + ")");
+            
+            if (want > 0) {
+                if (want <= _unavailableInboundBurst) {
+                    _availableInbound += want;
+                    _unavailableInboundBurst -= want;
+                } else {
+                    _availableInbound += _unavailableInboundBurst;
+                    _unavailableInboundBurst = 0;
+                }
+            }
         }
-        if (_availableOutboundBytes > _maxOutboundBytes) {
-            _totalWastedOutboundBytes += (_availableOutboundBytes - _maxOutboundBytes);
-            _availableOutboundBytes = _maxOutboundBytes;
+        
+        if (_availableOutbound > _maxOutbound) {
+            if (_log.shouldLog(Log.DEBUG))
+                _log.debug("available outbound (" + _availableOutbound + ") exceeds our outbound burst (" + _maxOutbound + "), so no supplement");
+            _unavailableOutboundBurst += _availableOutbound - _maxOutbound;
+            _availableOutbound = _maxOutbound;
+            if (_unavailableOutboundBurst > _maxOutboundBurst) {
+                _totalWastedOutboundBytes += _unavailableOutboundBurst - _maxOutboundBurst;
+                _unavailableOutboundBurst = _maxOutboundBurst;
+            }
+        } else {
+            // try to pull in up to 1/10th of the max outbound rate (aka burst rate), since 
+            // we refill every 100ms
+            int want = _maxOutbound/10;
+            if (want > (_maxOutbound - _availableOutbound))
+                want = _maxOutbound - _availableOutbound;
+            if (_log.shouldLog(Log.DEBUG))
+                _log.debug("want to pull " + want + " from the outbound burst (" + _unavailableOutboundBurst + ") to supplement " + _availableOutbound + " (max: " + _maxOutbound + ")");
+            
+            if (want > 0) {
+                if (want <= _unavailableOutboundBurst) {
+                    _availableOutbound += want;
+                    _unavailableOutboundBurst -= want;
+                } else {
+                    _availableOutbound += _unavailableOutboundBurst;
+                    _unavailableOutboundBurst = 0;
+                }
+            }
         }
+        
         satisfyRequests();
         updateStats();
     }
@@ -204,15 +297,14 @@ public class FIFOBandwidthLimiter {
             if (_inboundUnlimited) {
                 satisfied = locked_satisfyInboundUnlimited();
             } else {
-                if (_availableInboundBytes > 0) {
+                if (_availableInbound > 0) {
                     satisfied = locked_satisfyInboundAvailable();
                 } else {
                     // no bandwidth available
                     if (_log.shouldLog(Log.DEBUG))
                         _log.debug("Still denying the " + _pendingInboundRequests.size() 
-                                  + " pending inbound requests (available "
-                                  + _availableInboundBytes + "/" + _availableOutboundBytes 
-                                  + " in/out, longest waited " + locked_getLongestInboundWait() + " in)");
+                                  + " pending inbound requests (status: " + getStatus().toString()
+                                  + ", longest waited " + locked_getLongestInboundWait() + " in)");
                 }
             }
         }
@@ -289,7 +381,7 @@ public class FIFOBandwidthLimiter {
         List satisfied = null;
         
         for (int i = 0; i < _pendingInboundRequests.size(); i++) {
-            if (_availableInboundBytes <= 0) break;
+            if (_availableInbound <= 0) break;
             SimpleRequest req = (SimpleRequest)_pendingInboundRequests.get(i);
             long waited = now() - req.getRequestTime();
             if (req.getAborted()) {
@@ -313,11 +405,11 @@ public class FIFOBandwidthLimiter {
             // ok, they are really waiting for us to give them stuff
             int requested = req.getPendingInboundRequested();
             int allocated = 0;
-            if (_availableInboundBytes > requested) 
+            if (_availableInbound > requested) 
                 allocated = requested;
             else
-                allocated = _availableInboundBytes;
-            _availableInboundBytes -= allocated;
+                allocated = _availableInbound;
+            _availableInbound -= allocated;
             _totalAllocatedInboundBytes += allocated;
             req.allocateBytes(allocated, 0);
             if (satisfied == null)
@@ -354,15 +446,14 @@ public class FIFOBandwidthLimiter {
             if (_outboundUnlimited) {
                 satisfied = locked_satisfyOutboundUnlimited();
             } else {
-                if (_availableOutboundBytes > 0) {
+                if (_availableOutbound > 0) {
                     satisfied = locked_satisfyOutboundAvailable();
                 } else {
                     // no bandwidth available
                     if (_log.shouldLog(Log.DEBUG))
                         _log.debug("Still denying the " + _pendingOutboundRequests.size() 
-                                  + " pending outbound requests (available "
-                                  + _availableInboundBytes + "/" + _availableOutboundBytes + " in/out, "
-                                  + "longest waited " + locked_getLongestOutboundWait() + " out)");
+                                  + " pending outbound requests (status: " + getStatus().toString()
+                                  + ", longest waited " + locked_getLongestOutboundWait() + " out)");
                 }
             }
         }
@@ -414,7 +505,7 @@ public class FIFOBandwidthLimiter {
         List satisfied = null;
         
         for (int i = 0; i < _pendingOutboundRequests.size(); i++) {
-            if (_availableOutboundBytes <= 0) break;
+            if (_availableOutbound <= 0) break;
             SimpleRequest req = (SimpleRequest)_pendingOutboundRequests.get(i);
             long waited = now() - req.getRequestTime();
             if (req.getAborted()) {
@@ -438,11 +529,11 @@ public class FIFOBandwidthLimiter {
             // ok, they are really waiting for us to give them stuff
             int requested = req.getPendingOutboundRequested();
             int allocated = 0;
-            if (_availableOutboundBytes > requested) 
+            if (_availableOutbound > requested) 
                 allocated = requested;
             else
-                allocated = _availableOutboundBytes;
-            _availableOutboundBytes -= allocated;
+                allocated = _availableOutbound;
+            _availableOutbound -= allocated;
             _totalAllocatedOutboundBytes += allocated;
             req.allocateBytes(0, allocated);
             if (satisfied == null)
@@ -476,9 +567,8 @@ public class FIFOBandwidthLimiter {
     public void renderStatusHTML(Writer out) throws IOException {
         long now = now();
         StringBuffer buf = new StringBuffer(4096);
-        buf.append("<br /><b>Pending bandwidth requests (with ");
-        buf.append(_availableInboundBytes).append('/');
-        buf.append(_availableOutboundBytes).append(" bytes inbound/outbound available):</b><ul>");
+        buf.append("<br /><i>Limiter status: ").append(getStatus().toString()).append("</i><br />\n");
+        buf.append("<b>Pending bandwidth requests:</b><ul>");
         buf.append("<li>Inbound requests: <ol>");
         synchronized (_pendingInboundRequests) {
             for (int i = 0; i < _pendingInboundRequests.size(); i++) {
diff --git a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java
index 37f47765ae..0218600414 100644
--- a/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java
+++ b/router/java/src/net/i2p/router/transport/FIFOBandwidthRefiller.java
@@ -11,8 +11,10 @@ class FIFOBandwidthRefiller implements Runnable {
     private int _inboundKBytesPerSecond;
     /** how many KBps do we want to allow? */
     private int _outboundKBytesPerSecond;
-    /** how frequently do we want to replenish the available queues? */
-    private long _replenishFrequency;
+    /** how many KBps do we want to allow during burst? */
+    private int _inboundBurstKBytesPerSecond;
+    /** how many KBps do we want to allow during burst? */
+    private int _outboundBurstKBytesPerSecond;
     /** when did we last replenish the queue? */
     private long _lastRefillTime;
     /** when did we last check the config for updates? */
@@ -22,6 +24,8 @@ class FIFOBandwidthRefiller implements Runnable {
  
     public static final String PROP_INBOUND_BANDWIDTH = "i2np.bandwidth.inboundKBytesPerSecond";
     public static final String PROP_OUTBOUND_BANDWIDTH = "i2np.bandwidth.outboundKBytesPerSecond";
+    public static final String PROP_INBOUND_BURST_BANDWIDTH = "i2np.bandwidth.inboundBurstKBytesPerSecond";
+    public static final String PROP_OUTBOUND_BURST_BANDWIDTH = "i2np.bandwidth.outboundBurstKBytesPerSecond";
     public static final String PROP_INBOUND_BANDWIDTH_PEAK = "i2np.bandwidth.inboundBurstKBytes";
     public static final String PROP_OUTBOUND_BANDWIDTH_PEAK = "i2np.bandwidth.outboundBurstKBytes";
     //public static final String PROP_REPLENISH_FREQUENCY = "i2np.bandwidth.replenishFrequencyMs";
@@ -29,6 +33,8 @@ class FIFOBandwidthRefiller implements Runnable {
     // no longer allow unlimited bandwidth - the user must specify a value, and if they do not, it is 16KBps
     public static final int DEFAULT_INBOUND_BANDWIDTH = 16;
     public static final int DEFAULT_OUTBOUND_BANDWIDTH = 16;
+    public static final int DEFAULT_INBOUND_BURST_BANDWIDTH = 32;
+    public static final int DEFAULT_OUTBOUND_BURST_BANDWIDTH = 32;
 
     public static final int DEFAULT_BURST_SECONDS = 60;
     
@@ -40,10 +46,12 @@ class FIFOBandwidthRefiller implements Runnable {
     public static final int MIN_INBOUND_BANDWIDTH_PEAK = 10;
     /** For now, until there is some tuning and safe throttling, we set the floor at a 10 second burst */
     public static final int MIN_OUTBOUND_BANDWIDTH_PEAK = 10;
-    /** Updating the bandwidth more than once a second is silly.  once every 2 or 5 seconds is less so. */
-    public static final long MIN_REPLENISH_FREQUENCY = 100;
     
-    private static final long DEFAULT_REPLENISH_FREQUENCY = 100;
+    /** 
+     * how often we replenish the queues.  
+     * the bandwidth limiter is configured to expect an update 10 times per second 
+     */
+    private static final long REPLENISH_FREQUENCY = 100;
     
     public FIFOBandwidthRefiller(I2PAppContext context, FIFOBandwidthLimiter limiter) {
         _limiter = limiter;
@@ -67,7 +75,7 @@ class FIFOBandwidthRefiller implements Runnable {
                 _lastRefillTime = now;
             }
             
-            try { Thread.sleep(_replenishFrequency); } catch (InterruptedException ie) {}
+            try { Thread.sleep(REPLENISH_FREQUENCY); } catch (InterruptedException ie) {}
         }
     }
     
@@ -80,12 +88,11 @@ class FIFOBandwidthRefiller implements Runnable {
     private boolean updateQueues(long now) {
         long numMs = (now - _lastRefillTime);
         if (_log.shouldLog(Log.INFO))
-            _log.info("Updating bandwidth after " + numMs + " (available in=" 
-                       + _limiter.getAvailableInboundBytes() + ", out=" 
-                       + _limiter.getAvailableOutboundBytes()+ ", rate in=" 
+            _log.info("Updating bandwidth after " + numMs + " (status: " + _limiter.getStatus().toString()
+                       + " rate in=" 
                        + _inboundKBytesPerSecond + ", out=" 
                        + _outboundKBytesPerSecond  +")");
-        if (numMs >= MIN_REPLENISH_FREQUENCY) {
+        if (numMs >= REPLENISH_FREQUENCY) {
             long inboundToAdd = (1024*_inboundKBytesPerSecond * numMs)/1000;
             long outboundToAdd = (1024*_outboundKBytesPerSecond * numMs)/1000;
 
@@ -122,10 +129,10 @@ class FIFOBandwidthRefiller implements Runnable {
     private void checkConfig() {
         updateInboundRate();
         updateOutboundRate();
+        updateInboundBurstRate();
+        updateOutboundBurstRate();
         updateInboundPeak();
         updateOutboundPeak();
-
-        _replenishFrequency = DEFAULT_REPLENISH_FREQUENCY;
         
         if (_inboundKBytesPerSecond <= 0) {
             _limiter.setInboundUnlimited(true);
@@ -196,69 +203,127 @@ class FIFOBandwidthRefiller implements Runnable {
             _outboundKBytesPerSecond = DEFAULT_OUTBOUND_BANDWIDTH;
     }
     
+    private void updateInboundBurstRate() {
+        String inBwStr = _context.getProperty(PROP_INBOUND_BURST_BANDWIDTH);
+        if ( (inBwStr != null) && 
+             (inBwStr.trim().length() > 0) && 
+             (!(inBwStr.equals(String.valueOf(_inboundBurstKBytesPerSecond)))) ) {
+            // bandwidth was specified *and* changed
+            try {
+                int in = Integer.parseInt(inBwStr);
+                if ( (in <= 0) || (in > MIN_INBOUND_BANDWIDTH) ) 
+                    _inboundBurstKBytesPerSecond = in;
+                else
+                    _inboundBurstKBytesPerSecond = MIN_INBOUND_BANDWIDTH;
+                if (_log.shouldLog(Log.DEBUG))
+                    _log.debug("Updating inbound burst rate to " + _inboundBurstKBytesPerSecond);
+            } catch (NumberFormatException nfe) {
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("Invalid inbound bandwidth burst limit [" + inBwStr 
+                              + "], keeping as " + _inboundBurstKBytesPerSecond);
+            }
+        } else {
+            if ( (inBwStr == null) && (_log.shouldLog(Log.DEBUG)) )
+                _log.debug("Inbound bandwidth burst limits not specified in the config via " + PROP_INBOUND_BURST_BANDWIDTH);
+        }
+        
+        if (_inboundBurstKBytesPerSecond <= 0)
+            _inboundBurstKBytesPerSecond = DEFAULT_INBOUND_BURST_BANDWIDTH;
+        _limiter.setInboundBurstKBps(_inboundBurstKBytesPerSecond);
+    }
+    
+    private void updateOutboundBurstRate() {
+        String outBwStr = _context.getProperty(PROP_OUTBOUND_BURST_BANDWIDTH);
+        
+        if ( (outBwStr != null) && 
+             (outBwStr.trim().length() > 0) && 
+             (!(outBwStr.equals(String.valueOf(_outboundBurstKBytesPerSecond)))) ) {
+            // bandwidth was specified *and* changed
+            try {
+                int out = Integer.parseInt(outBwStr);
+                if ( (out <= 0) || (out >= MIN_OUTBOUND_BANDWIDTH) )
+                    _outboundBurstKBytesPerSecond = out;
+                else
+                    _outboundBurstKBytesPerSecond = MIN_OUTBOUND_BANDWIDTH;
+                if (_log.shouldLog(Log.DEBUG))
+                    _log.debug("Updating outbound burst rate to " + _outboundBurstKBytesPerSecond);
+            } catch (NumberFormatException nfe) {
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("Invalid outbound bandwidth burst limit [" + outBwStr 
+                              + "], keeping as " + _outboundBurstKBytesPerSecond);
+            }
+        } else {
+            if ( (outBwStr == null) && (_log.shouldLog(Log.DEBUG)) )
+                _log.debug("Outbound bandwidth burst limits not specified in the config via " + PROP_OUTBOUND_BURST_BANDWIDTH);
+        }
+        
+        if (_outboundBurstKBytesPerSecond <= 0)
+            _outboundBurstKBytesPerSecond = DEFAULT_OUTBOUND_BURST_BANDWIDTH;
+        _limiter.setOutboundBurstKBps(_outboundBurstKBytesPerSecond);
+    }
+    
     private void updateInboundPeak() {
         String inBwStr = _context.getProperty(PROP_INBOUND_BANDWIDTH_PEAK);
         if ( (inBwStr != null) && 
              (inBwStr.trim().length() > 0) && 
-             (!(inBwStr.equals(String.valueOf(_limiter.getMaxInboundBytes())))) ) {
+             (!(inBwStr.equals(String.valueOf(_limiter.getInboundBurstBytes())))) ) {
             // peak bw was specified *and* changed
             try {
                 int in = Integer.parseInt(inBwStr);
                 if (in >= MIN_INBOUND_BANDWIDTH_PEAK) {
-                    if (in < _inboundKBytesPerSecond)
-                        _limiter.setMaxInboundBytes(_inboundKBytesPerSecond * 1024);
+                    if (in < _inboundBurstKBytesPerSecond)
+                        _limiter.setInboundBurstBytes(_inboundBurstKBytesPerSecond * 1024);
                     else 
-                        _limiter.setMaxInboundBytes(in * 1024);
+                        _limiter.setInboundBurstBytes(in * 1024);
                 } else {
-                    if (MIN_INBOUND_BANDWIDTH_PEAK < _inboundKBytesPerSecond) 
-                        _limiter.setMaxInboundBytes(_inboundKBytesPerSecond * 1024);
+                    if (MIN_INBOUND_BANDWIDTH_PEAK < _inboundBurstKBytesPerSecond) 
+                        _limiter.setInboundBurstBytes(_inboundBurstKBytesPerSecond * 1024);
                     else
-                        _limiter.setMaxInboundBytes(MIN_INBOUND_BANDWIDTH_PEAK * 1024);
+                        _limiter.setInboundBurstBytes(MIN_INBOUND_BANDWIDTH_PEAK * 1024);
                 }
             } catch (NumberFormatException nfe) {
                 if (_log.shouldLog(Log.WARN))
                     _log.warn("Invalid inbound bandwidth burst limit [" + inBwStr 
                               + "]");
-                _limiter.setMaxInboundBytes(DEFAULT_BURST_SECONDS * _inboundKBytesPerSecond * 1024);
+                _limiter.setInboundBurstBytes(DEFAULT_BURST_SECONDS * _inboundBurstKBytesPerSecond * 1024);
             }
         } else {
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("Inbound bandwidth burst limits not specified in the config via " 
                            + PROP_INBOUND_BANDWIDTH_PEAK);
-            _limiter.setMaxInboundBytes(DEFAULT_BURST_SECONDS * _inboundKBytesPerSecond * 1024);
+            _limiter.setInboundBurstBytes(DEFAULT_BURST_SECONDS * _inboundBurstKBytesPerSecond * 1024);
         }
     }
     private void updateOutboundPeak() {
-        String outBwStr = _context.getProperty(PROP_OUTBOUND_BANDWIDTH_PEAK);
-        if ( (outBwStr != null) && 
-             (outBwStr.trim().length() > 0) && 
-             (!(outBwStr.equals(String.valueOf(_limiter.getMaxOutboundBytes())))) ) {
+        String inBwStr = _context.getProperty(PROP_OUTBOUND_BANDWIDTH_PEAK);
+        if ( (inBwStr != null) && 
+             (inBwStr.trim().length() > 0) && 
+             (!(inBwStr.equals(String.valueOf(_limiter.getOutboundBurstBytes())))) ) {
             // peak bw was specified *and* changed
             try {
-                int out = Integer.parseInt(outBwStr);
-                if (out >= MIN_OUTBOUND_BANDWIDTH_PEAK) {
-                    if (out < _outboundKBytesPerSecond)
-                        _limiter.setMaxOutboundBytes(_outboundKBytesPerSecond * 1024);
-                    else
-                        _limiter.setMaxOutboundBytes(out * 1024);
+                int in = Integer.parseInt(inBwStr);
+                if (in >= MIN_OUTBOUND_BANDWIDTH_PEAK) {
+                    if (in < _outboundBurstKBytesPerSecond)
+                        _limiter.setOutboundBurstBytes(_outboundBurstKBytesPerSecond * 1024);
+                    else 
+                        _limiter.setOutboundBurstBytes(in * 1024);
                 } else {
-                    if (MIN_OUTBOUND_BANDWIDTH_PEAK < _outboundKBytesPerSecond)
-                        _limiter.setMaxOutboundBytes(_outboundKBytesPerSecond * 1024);
+                    if (MIN_OUTBOUND_BANDWIDTH_PEAK < _outboundBurstKBytesPerSecond) 
+                        _limiter.setOutboundBurstBytes(_outboundBurstKBytesPerSecond * 1024);
                     else
-                        _limiter.setMaxOutboundBytes(MIN_OUTBOUND_BANDWIDTH_PEAK * 1024);
+                        _limiter.setOutboundBurstBytes(MIN_OUTBOUND_BANDWIDTH_PEAK * 1024);
                 }
             } catch (NumberFormatException nfe) {
                 if (_log.shouldLog(Log.WARN))
-                    _log.warn("Invalid outbound bandwidth burst limit [" + outBwStr 
+                    _log.warn("Invalid outbound bandwidth burst limit [" + inBwStr 
                               + "]");
-                _limiter.setMaxOutboundBytes(DEFAULT_BURST_SECONDS * _outboundKBytesPerSecond * 1024);
+                _limiter.setOutboundBurstBytes(DEFAULT_BURST_SECONDS * _outboundBurstKBytesPerSecond * 1024);
             }
         } else {
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("Outbound bandwidth burst limits not specified in the config via " 
                            + PROP_OUTBOUND_BANDWIDTH_PEAK);
-            _limiter.setMaxOutboundBytes(DEFAULT_BURST_SECONDS * _outboundKBytesPerSecond * 1024);
+            _limiter.setOutboundBurstBytes(DEFAULT_BURST_SECONDS * _outboundBurstKBytesPerSecond * 1024);
         }
     }
-
 }
\ No newline at end of file
-- 
GitLab