diff --git a/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java b/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java
index 6a2f6223c9ce440a9e23b7c325c2d2d3f2b4f1ba..ec56b58ce31170d30134b176287228e9171350cf 100644
--- a/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java
+++ b/router/java/src/net/i2p/router/peermanager/CapacityCalculator.java
@@ -34,9 +34,10 @@ class CapacityCalculator {
     
     public static double calc(PeerProfile profile) {
         double capacity;
-
+        RouterContext context = profile.getContext();
+        long now = context.clock().now();
         TunnelHistory history = profile.getTunnelHistory();
-        if (tooOld(profile)) { 
+        if (tooOld(profile, now)) {
             capacity = 1;
         } else {
             RateStat acceptStat = profile.getTunnelCreateResponseTime();
@@ -51,22 +52,22 @@ class CapacityCalculator {
                 double capacity30m = estimateCapacity(acceptStat, rejectStat, failedStat, 30*60*1000);
                 double capacity60m = estimateCapacity(acceptStat, rejectStat, failedStat, 60*60*1000);
                 double capacity1d  = estimateCapacity(acceptStat, rejectStat, failedStat, 24*60*60*1000);
-        
+
+                // now take into account recent tunnel rejections
+                long cutoff = now - PeerManager.REORGANIZE_TIME_LONG;
+                if (history.getLastRejectedProbabalistic() > cutoff) {
+                    capacity10m /= 2;
+                } else if (history.getLastRejectedTransient() > cutoff) {
+                    // never happens
+                    capacity10m /= 4;
+                }
+
                 capacity = capacity10m * periodWeight(10*60*1000) + 
                            capacity30m * periodWeight(30*60*1000) + 
                            capacity60m * periodWeight(60*60*1000) + 
                            capacity1d  * periodWeight(24*60*60*1000);
             }
-        }        
-        
-        // now take into account non-rejection tunnel rejections (which haven't 
-        // incremented the rejection counter, since they were only temporary)
-        RouterContext context = profile.getContext();
-        long now = context.clock().now();
-        if (history.getLastRejectedTransient() > now - 5*60*1000)
-            capacity = 1;
-        else if (history.getLastRejectedProbabalistic() > now - 5*60*1000)
-            capacity -= context.random().nextInt(5);
+        }
 
         // boost new profiles
         if (now - profile.getFirstHeardAbout() < 45*60*1000)
@@ -115,11 +116,8 @@ class CapacityCalculator {
      * If we haven't heard from them in an hour, they aren't too useful.
      *
      */
-    private static boolean tooOld(PeerProfile profile) {
-        if (profile.getIsActive(60*60*1000)) 
-            return false;
-        else 
-            return true;
+    private static boolean tooOld(PeerProfile profile, long now) {
+        return !profile.getIsActive(60*60*1000, now);
     }
     
     /**
diff --git a/router/java/src/net/i2p/router/peermanager/PeerManager.java b/router/java/src/net/i2p/router/peermanager/PeerManager.java
index c4c2459002f5286993feb6081c1664190158fbab..d488f30315472c8289f841dfb845af0a9ec82b33 100644
--- a/router/java/src/net/i2p/router/peermanager/PeerManager.java
+++ b/router/java/src/net/i2p/router/peermanager/PeerManager.java
@@ -54,7 +54,7 @@ class PeerManager {
      *  This must also be less than 10 minutes, which is the shortest
      *  Rate contained in the profile, as the Rates must be coalesced.
      */
-    private static final long REORGANIZE_TIME_LONG = 351*1000;
+    static final long REORGANIZE_TIME_LONG = 351*1000;
     /** After first two hours of uptime ~= 246 */
     static final int REORGANIZES_PER_DAY = (int) (24*60*60*1000L / REORGANIZE_TIME_LONG);
     private static final long STORE_TIME = 19*60*60*1000;
diff --git a/router/java/src/net/i2p/router/peermanager/PeerProfile.java b/router/java/src/net/i2p/router/peermanager/PeerProfile.java
index b8ba4d2e2da1dbe0bbedc09dd4ba64c756a4c511..1e7c40c060da8be0618c42220f2bd2f636a816be 100644
--- a/router/java/src/net/i2p/router/peermanager/PeerProfile.java
+++ b/router/java/src/net/i2p/router/peermanager/PeerProfile.java
@@ -166,7 +166,17 @@ public class PeerProfile {
      * 5 minutes)
      */
     public boolean getIsActive() {
-        return getIsActive(5*60*1000);
+        return getIsActive(5*60*1000, _context.clock().now());
+    }
+    
+    /**
+     * Is this peer active at the moment (sending/receiving messages within the last
+     * 5 minutes)
+     *
+     * @since 0.9.58
+     */
+    public boolean getIsActive(long now) {
+        return getIsActive(5*60*1000, now);
     }
     
     /** @since 0.8.11 */
@@ -222,17 +232,11 @@ public class PeerProfile {
      *
      * @param period must be one of the periods in the RateStat constructors below
      *        (5*60*1000 or 60*60*1000)
+     *
+     * @since 0.9.58
      */
-    public boolean getIsActive(long period) {
-        //if ( (getSendSuccessSize().getRate(period).getCurrentEventCount() > 0) ||
-        //     (getSendSuccessSize().getRate(period).getLastEventCount() > 0) ||
-        //     (getReceiveSize().getRate(period).getCurrentEventCount() > 0) ||
-        //     (getReceiveSize().getRate(period).getLastEventCount() > 0) ||
-        //     _context.commSystem().isEstablished(_peer) )
-        //    return true;
-        //else
-        //    return false;
-        long before = _context.clock().now() - period;
+    public boolean getIsActive(long period, long now) {
+        long before = now - period;
         return getLastHeardFrom() < before ||
                getLastSendSuccessful() < before ||
                isEstablished();
@@ -542,12 +546,8 @@ public class PeerProfile {
      */
     public synchronized void expandProfile() {
         String group = (null == _peer ? "profileUnknown" : _peer.toBase64().substring(0,6));
-        //if (_sendSuccessSize == null)
-        //    _sendSuccessSize = new RateStat("sendSuccessSize", "How large successfully sent messages are", group, new long[] { 5*60*1000l, 60*60*1000l });
-        //if (_receiveSize == null)
-        //    _receiveSize = new RateStat("receiveSize", "How large received messages are", group, new long[] { 5*60*1000l, 60*60*1000l } );
         if (_tunnelCreateResponseTime == null)
-            _tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (in milliseconds)", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000 } );
+            _tunnelCreateResponseTime = new RateStat("tunnelCreateResponseTime", "how long it takes to get a tunnel create response from the peer (ms)", group, TunnelHistory.RATES);
 
         if (ENABLE_TUNNEL_TEST_RESPONSE_TIME && _tunnelTestResponseTime == null)
             _tunnelTestResponseTime = new RateStat("tunnelTestResponseTime", "how long it takes to successfully test a tunnel this peer participates in (in milliseconds)", group, new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 3*60*60*1000l, 24*60*60*1000 } );
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
index ce23263de352e8f7010c3c6a2c5babe8960437d6..43b9f5f140e2342cc7dd5f7e290dcc4cf4c24a41 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
@@ -902,6 +902,7 @@ public class ProfileOrganizer {
         if (numToPromote > 0) {
             if (_log.shouldLog(Log.INFO))
                 _log.info("Need to explicitly promote " + numToPromote + " peers to the fast group");
+            long now = _context.clock().now();
             for (PeerProfile cur : _strictCapacityOrder) {
                 if ( (!_fastPeers.containsKey(cur.getPeer())) && (!cur.getIsFailing()) ) {
                     if (!isSelectable(cur.getPeer())) {
@@ -910,7 +911,7 @@ public class ProfileOrganizer {
                         //     _log.info("skip unknown peer from fast promotion: " + cur.getPeer().toBase64());
                         continue;
                     }
-                    if (!cur.getIsActive()) {
+                    if (!cur.getIsActive(now)) {
                         // skip inactive
                         // if (_log.shouldLog(Log.INFO))
                         //     _log.info("skip inactive peer from fast promotion: " + cur.getPeer().toBase64());
@@ -991,8 +992,9 @@ public class ProfileOrganizer {
      */
     private void locked_unfailAsNecessary() {
         int notFailingActive = 0;
+        long now = _context.clock().now();
         for (PeerProfile peer : _notFailingPeers.values()) {
-            if (peer.getIsActive())
+            if (peer.getIsActive(now))
                 notFailingActive++;
             if (notFailingActive >= MIN_NOT_FAILING_ACTIVE) {
                 // we've got enough, no need to try further
@@ -1005,7 +1007,7 @@ public class ProfileOrganizer {
         if (needToUnfail > 0) {
             int unfailed = 0;
             for (PeerProfile best : _strictCapacityOrder) {
-                if ( (best.getIsActive()) && (best.getIsFailing()) ) {
+                if ( (best.getIsActive(now)) && (best.getIsFailing()) ) {
                     if (_log.shouldLog(Log.WARN))
                         _log.warn("All peers were failing, so we have overridden the failing flag for one of the most reliable active peers (" + best.getPeer().toBase64() + ")");
                     best.setIsFailing(false);
@@ -1035,11 +1037,12 @@ public class ProfileOrganizer {
         double totalCapacity = 0;
         double totalIntegration = 0;
         Set<PeerProfile> reordered = new TreeSet<PeerProfile>(_comp);
+        long now = _context.clock().now();
         for (PeerProfile profile : allPeers) {
             if (_us.equals(profile.getPeer())) continue;
             
             // only take into account active peers that aren't failing
-            if (profile.getIsFailing() || (!profile.getIsActive()))
+            if (profile.getIsFailing() || (!profile.getIsActive(now)))
                 continue;
         
             // dont bother trying to make sense of things below the baseline
@@ -1557,9 +1560,10 @@ public class ProfileOrganizer {
         organizer.reorganize();
         DecimalFormat fmt = new DecimalFormat("0000.0");
         
+        long now = ctx.clock().now();
         for (Hash peer : organizer.selectAllPeers()) {
             PeerProfile profile = organizer.getProfile(peer);
-            if (!profile.getIsActive()) {
+            if (!profile.getIsActive(now)) {
                 System.out.println("Peer " + peer.toBase64().substring(0,4) 
                            + " [" + (organizer.isFast(peer) ? "IF+R" : 
                                      organizer.isHighCapacity(peer) ? "IR  " :
@@ -1567,7 +1571,7 @@ public class ProfileOrganizer {
                            + " Speed:\t" + fmt.format(profile.getSpeedValue())
                            + " Capacity:\t" + fmt.format(profile.getCapacityValue())
                            + " Integration:\t" + fmt.format(profile.getIntegrationValue())
-                           + " Active?\t" + profile.getIsActive() 
+                           + " Active?\t" + profile.getIsActive(now) 
                            + " Failing?\t" + profile.getIsFailing());
             } else {
                 System.out.println("Peer " + peer.toBase64().substring(0,4) 
@@ -1577,7 +1581,7 @@ public class ProfileOrganizer {
                            + " Speed:\t" + fmt.format(profile.getSpeedValue())
                            + " Capacity:\t" + fmt.format(profile.getCapacityValue())
                            + " Integration:\t" + fmt.format(profile.getIntegrationValue())
-                           + " Active?\t" + profile.getIsActive() 
+                           + " Active?\t" + profile.getIsActive(now) 
                            + " Failing?\t" + profile.getIsFailing());
             }
         }
diff --git a/router/java/src/net/i2p/router/peermanager/TunnelHistory.java b/router/java/src/net/i2p/router/peermanager/TunnelHistory.java
index b4ebb740558adfe16c6c9bd6b9e589d6962f3241..5f44c45c9b6cf2e91ce577dfe56fe6dfc43e9876 100644
--- a/router/java/src/net/i2p/router/peermanager/TunnelHistory.java
+++ b/router/java/src/net/i2p/router/peermanager/TunnelHistory.java
@@ -26,9 +26,10 @@ public class TunnelHistory {
     private volatile long _lastRejectedProbabalistic;
     private final AtomicLong _lifetimeFailed = new AtomicLong();
     private volatile long _lastFailed;
-    private RateStat _rejectRate;
-    private RateStat _failRate;
+    private final RateStat _rejectRate;
+    private final RateStat _failRate;
     private final String _statGroup;
+    static final long[] RATES = new long[] { 10*60*1000l, 30*60*1000l, 60*60*1000l, 24*60*60*1000l };
     
     /** probabalistic tunnel rejection due to a flood of requests - infrequent */
     public static final int TUNNEL_REJECT_PROBABALISTIC_REJECT = 10;
@@ -43,12 +44,8 @@ public class TunnelHistory {
         _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 = new RateStat("tunnelHistory.rejectRate", "How often does this peer reject a tunnel request?", statGroup, RATES);
+        _failRate = new RateStat("tunnelHistory.failRate", "How often do tunnels this peer accepts fail?", statGroup, RATES);
     }
     
     /** total tunnels the peer has agreed to participate in */
@@ -85,19 +82,19 @@ public class TunnelHistory {
      */
     public void incrementRejected(int severity) {
         _lifetimeRejected.incrementAndGet();
+        long now = _context.clock().now();
         if (severity >= TUNNEL_REJECT_CRIT) {
-            _lastRejectedCritical = _context.clock().now();
-            _rejectRate.addData(1);
+            _lastRejectedCritical = now;
         } else if (severity >= TUNNEL_REJECT_BANDWIDTH) {
-            _lastRejectedBandwidth = _context.clock().now();
-            _rejectRate.addData(1);
+            _lastRejectedBandwidth = now;
         } else if (severity >= TUNNEL_REJECT_TRANSIENT_OVERLOAD) {
-            _lastRejectedTransient = _context.clock().now();
-            // dont increment the reject rate in this case
+            _lastRejectedTransient = now;
         } else if (severity >= TUNNEL_REJECT_PROBABALISTIC_REJECT) {
-            _lastRejectedProbabalistic = _context.clock().now();
-            // dont increment the reject rate in this case
+            _lastRejectedProbabalistic = now;
         }
+        // a rejection is always a rejection, don't factor based on severity,
+        // which could impact our ability to avoid a congested peer
+        _rejectRate.addData(1);
     }
 
     /**
@@ -194,14 +191,9 @@ public class TunnelHistory {
         _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);
+            _log.warn("TunnelHistory rates are corrupt", iae);
         }
     }