diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java
index 4ca03b1e5..0f2188bff 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java
@@ -59,6 +59,8 @@ public class ConfigLoggingHelper {
buf.append(prefix).append('=').append(level).append('\n');
}
buf.append(" \n");
+ buf.append("Add additional logging statements above. Example: net.i2p.router.tunnel=WARN ");
+ buf.append("Or put entries in the logger.config file. Example: logger.record.net.i2p.router.tunnel=WARN ");
buf.append("Valid levels are DEBUG, INFO, WARN, ERROR, CRIT\n");
return buf.toString();
}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHandler.java
index 73ebcf87e..c64fbc4e9 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigTunnelsHandler.java
@@ -135,7 +135,7 @@ public class ConfigTunnelsHandler extends FormHandler {
if (saveRequired) {
boolean saved = _context.router().saveConfig();
if (saved)
- addFormNotice("Configuration saved successfully");
+ addFormNotice("Exploratory tunnel configuration saved successfully");
else
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/JobQueueHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/JobQueueHelper.java
new file mode 100644
index 000000000..a56cce19a
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/JobQueueHelper.java
@@ -0,0 +1,46 @@
+package net.i2p.router.web;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import net.i2p.router.RouterContext;
+
+public class JobQueueHelper {
+ private RouterContext _context;
+ private Writer _out;
+ /**
+ * Configure this bean to query a particular router context
+ *
+ * @param contextId begging few characters of the routerHash, or null to pick
+ * the first one we come across.
+ */
+ public void setContextId(String contextId) {
+ try {
+ _context = ContextHelper.getContext(contextId);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ public JobQueueHelper() {}
+
+ public void setWriter(Writer writer) { _out = writer; }
+
+ public String getJobQueueSummary() {
+ try {
+ if (_out != null) {
+ _context.jobQueue().renderStatusHTML(_out);
+ return "";
+ } else {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
+ _context.jobQueue().renderStatusHTML(new OutputStreamWriter(baos));
+ return new String(baos.toByteArray());
+ }
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ return "";
+ }
+ }
+}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
index 1c26f5bd0..bcaee116f 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -83,14 +83,23 @@ public class SummaryHelper {
long ms = _context.clock().getOffset();
- if (ms < 60 * 1000) {
- return now + " (" + (ms / 1000) + "s)";
- } else if (ms < 60 * 60 * 1000) {
- return now + " (" + (ms / (60 * 1000)) + "m)";
- } else if (ms < 24 * 60 * 60 * 1000) {
- return now + " (" + (ms / (60 * 60 * 1000)) + "h)";
+ long diff = ms;
+ if (diff < 0)
+ diff = 0 - diff;
+ if (diff == 0) {
+ return now + " (no skew)";
+ } else if (diff < 1000) {
+ return now + " (" + ms + "ms skew)";
+ } else if (diff < 5 * 1000) {
+ return now + " (" + (ms / 1000) + "s skew)";
+ } else if (diff < 60 * 1000) {
+ return now + " (" + (ms / 1000) + "s skew)";
+ } else if (diff < 60 * 60 * 1000) {
+ return now + " (" + (ms / (60 * 1000)) + "m skew)";
+ } else if (diff < 24 * 60 * 60 * 1000) {
+ return now + " (" + (ms / (60 * 60 * 1000)) + "h skew)";
} else {
- return now + " (" + (ms / (24 * 60 * 60 * 1000)) + "d)";
+ return now + " (" + (ms / (24 * 60 * 60 * 1000)) + "d skew)";
}
}
@@ -408,6 +417,28 @@ public class SummaryHelper {
return _context.tunnelManager().getOutboundTunnelCount();
}
+ /**
+ * How many inbound client tunnels we have.
+ *
+ */
+ public int getInboundClientTunnels() {
+ if (_context == null)
+ return 0;
+ else
+ return _context.tunnelManager().getInboundClientTunnelCount();
+ }
+
+ /**
+ * How many active outbound client tunnels we have.
+ *
+ */
+ public int getOutboundClientTunnels() {
+ if (_context == null)
+ return 0;
+ else
+ return _context.tunnelManager().getOutboundClientTunnelCount();
+ }
+
/**
* How many tunnels we are participating in.
*
@@ -459,4 +490,4 @@ public class SummaryHelper {
public boolean updateAvailable() {
return NewsFetcher.getInstance(_context).updateAvailable();
}
-}
\ No newline at end of file
+}
diff --git a/apps/routerconsole/jsp/configtunnels.jsp b/apps/routerconsole/jsp/configtunnels.jsp
index 84ecf4341..0b6fcb714 100644
--- a/apps/routerconsole/jsp/configtunnels.jsp
+++ b/apps/routerconsole/jsp/configtunnels.jsp
@@ -33,6 +33,9 @@
+ Note - Exploratory tunnel setting changes are stored in the router.config file.
+ Client tunnel changes are temporary and are not saved.
+ To make permanent client tunnel changes see the i2ptunnel page.
diff --git a/apps/routerconsole/jsp/jobs.jsp b/apps/routerconsole/jsp/jobs.jsp
new file mode 100644
index 000000000..56701af6b
--- /dev/null
+++ b/apps/routerconsole/jsp/jobs.jsp
@@ -0,0 +1,21 @@
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+
+
+
+I2P Router Console - job queue
+
+
+
+<%@include file="nav.jsp" %>
+<%@include file="summary.jsp" %>
+
+
+
+ " />
+
+
+
+
+
+
diff --git a/apps/routerconsole/jsp/nav.jsp b/apps/routerconsole/jsp/nav.jsp
index 414b7c7f2..57c826466 100644
--- a/apps/routerconsole/jsp/nav.jsp
+++ b/apps/routerconsole/jsp/nav.jsp
@@ -18,11 +18,13 @@
Susimail |
SusiDNS |
Syndie |
+ My Eepsite I2PTunnel |
Tunnels |
Profiles |
NetDB |
Logs |
+ Jobs |
Stats |
Internals
diff --git a/apps/routerconsole/jsp/summary.jsp b/apps/routerconsole/jsp/summary.jsp
index ad45e9328..9d246ddb6 100644
--- a/apps/routerconsole/jsp/summary.jsp
+++ b/apps/routerconsole/jsp/summary.jsp
@@ -72,9 +72,9 @@
- Tunnels
- Inbound:
- Outbound:
+ Tunnels in/out
+ Exploratory:/
+ Client:/ Participating:
diff --git a/history.txt b/history.txt
index bc98dcadb..553e0093e 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,25 @@
-$Id: history.txt,v 1.350 2005/12/07 19:50:35 jrandom Exp $
+$Id: history.txt,v 1.351 2005/12/08 15:53:43 jrandom Exp $
+
+2005-12-09 zzz
+ * Create different strategies for exploratory tunnels (which are difficult
+ to create) and client tunnels (which are much easier)
+ * Gradually increase number of parallel build attempts as tunnel expiry
+ nears.
+ * Temporarily shorten attempted build tunnel length if builds using
+ configured tunnel length are unsuccessful
+ * React more aggressively to tunnel failure than routine tunnel
+ replacement
+ * Make tunnel creation times randomized - there is existing code to
+ randomize the tunnels but it isn't effective due to the tunnel creation
+ strategy. Currently, most tunnels get built all at once, at about 2 1/2
+ to 3 minutes before expiration. The patch fixes this by fixing the
+ randomization, and by changing the overlap time (with old tunnels) to a
+ range of 2 to 4 minutes.
+ * Reduce number of excess tunnels. Lots of excess tunnels get created due
+ to overlapping calls. Just about anything generated a call which could
+ build many tunnels all at once, even if tunnel building was already in
+ process.
+ * Miscellaneous router console enhancements
2005-12-08 jrandom
* Minor bugfix in SSU for dealing with corrupt packets
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 12b75cd61..dff197ec6 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.307 $ $Date: 2005/12/01 12:16:54 $";
+ public final static String ID = "$Revision: 1.308 $ $Date: 2005/12/08 15:53:41 $";
public final static String VERSION = "0.6.1.7";
- public final static long BUILD = 1;
+ public final static long BUILD = 2;
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/RouterWatchdog.java b/router/java/src/net/i2p/router/RouterWatchdog.java
index eafdd46d1..f8f78a763 100644
--- a/router/java/src/net/i2p/router/RouterWatchdog.java
+++ b/router/java/src/net/i2p/router/RouterWatchdog.java
@@ -90,7 +90,9 @@ class RouterWatchdog implements Runnable {
public void monitorRouter() {
boolean ok = verifyJobQueueLiveliness();
- ok = ok && verifyClientLiveliness();
+ // If we aren't connected to the network that's why there's nobody to talk to
+ int netErrors = (int) _context.statManager().getRate("udp.sendException").getRate(60*1000).getLastEventCount();
+ ok = ok && (verifyClientLiveliness() || netErrors >= 5);
if (!ok) {
dumpStatus();
diff --git a/router/java/src/net/i2p/router/TunnelManagerFacade.java b/router/java/src/net/i2p/router/TunnelManagerFacade.java
index 67a2c4033..052af69b8 100644
--- a/router/java/src/net/i2p/router/TunnelManagerFacade.java
+++ b/router/java/src/net/i2p/router/TunnelManagerFacade.java
@@ -48,6 +48,10 @@ public interface TunnelManagerFacade extends Service {
public int getFreeTunnelCount();
/** how many outbound tunnels do we have available? */
public int getOutboundTunnelCount();
+ /** how many free inbound client tunnels do we have available? */
+ public int getInboundClientTunnelCount();
+ /** how many outbound client tunnels do we have available? */
+ public int getOutboundClientTunnelCount();
/** When does the last tunnel we are participating in expire? */
public long getLastParticipatingExpiration();
@@ -81,6 +85,8 @@ class DummyTunnelManagerFacade implements TunnelManagerFacade {
public int getParticipatingCount() { return 0; }
public int getFreeTunnelCount() { return 0; }
public int getOutboundTunnelCount() { return 0; }
+ public int getInboundClientTunnelCount() { return 0; }
+ public int getOutboundClientTunnelCount() { return 0; }
public long getLastParticipatingExpiration() { return -1; }
public void buildTunnels(Destination client, ClientTunnelSettings settings) {}
public TunnelPoolSettings getInboundSettings() { return null; }
diff --git a/router/java/src/net/i2p/router/TunnelPoolSettings.java b/router/java/src/net/i2p/router/TunnelPoolSettings.java
index 8df755a28..aaeb5cec3 100644
--- a/router/java/src/net/i2p/router/TunnelPoolSettings.java
+++ b/router/java/src/net/i2p/router/TunnelPoolSettings.java
@@ -18,6 +18,7 @@ public class TunnelPoolSettings {
private int _duration;
private int _length;
private int _lengthVariance;
+ private int _lengthOverride;
private boolean _isInbound;
private boolean _isExploratory;
private boolean _allowZeroHop;
@@ -54,6 +55,7 @@ public class TunnelPoolSettings {
_duration = DEFAULT_DURATION;
_length = DEFAULT_LENGTH;
_lengthVariance = DEFAULT_LENGTH_VARIANCE;
+ _lengthOverride = 0;
_allowZeroHop = DEFAULT_ALLOW_ZERO_HOP;
_isInbound = false;
_isExploratory = false;
@@ -90,6 +92,10 @@ public class TunnelPoolSettings {
*/
public int getLengthVariance() { return _lengthVariance; }
public void setLengthVariance(int variance) { _lengthVariance = variance; }
+
+ /* Set to a nonzero value to override the length setting */
+ public int getLengthOverride() { return _lengthOverride; }
+ public void setLengthOverride(int variance) { _lengthOverride = variance; }
/** is this an inbound tunnel? */
public boolean isInbound() { return _isInbound; }
diff --git a/router/java/src/net/i2p/router/tunnel/pool/ExpireJob.java b/router/java/src/net/i2p/router/tunnel/pool/ExpireJob.java
index 83ec39570..93dff3b54 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/ExpireJob.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/ExpireJob.java
@@ -25,11 +25,16 @@ class ExpireJob extends JobImpl {
return "Expire exploratory outbound tunnel";
}
} else {
- if (_pool.getSettings().isInbound()) {
- return "Expire client inbound tunnel";
- } else {
- return "Expire client outbound tunnel";
- }
+ StringBuffer rv = new StringBuffer(32);
+ if (_pool.getSettings().isInbound())
+ rv.append("Expire inbound client tunnel for ");
+ else
+ rv.append("Expire outbound client tunnel for ");
+ if (_pool.getSettings().getDestinationNickname() != null)
+ rv.append(_pool.getSettings().getDestinationNickname());
+ else
+ rv.append(_pool.getSettings().getDestination().toBase64().substring(0,4));
+ return rv.toString();
}
}
public void runJob() {
@@ -44,4 +49,4 @@ class ExpireJob extends JobImpl {
getContext().tunnelDispatcher().remove(_cfg);
}
}
-}
\ No newline at end of file
+}
diff --git a/router/java/src/net/i2p/router/tunnel/pool/RebuildJob.java b/router/java/src/net/i2p/router/tunnel/pool/RebuildJob.java
index fb5cad7cb..fd4a6eda3 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/RebuildJob.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/RebuildJob.java
@@ -7,7 +7,10 @@ import net.i2p.router.tunnel.TunnelCreatorConfig;
/**
* Build a new tunnel to replace the existing one before it expires. This job
* should be removed (or scheduled to run immediately) if the tunnel fails.
- *
+ * If an exploratory tunnel build at a random time between 3 1/2 and 4 minutes early;
+ * else if only one tunnel in pool build 4 minutes early;
+ * otherwise build at a random time between 2 and 4 minutes early.
+ * Five build attempts in parallel if an exploratory tunnel.
*/
class RebuildJob extends JobImpl {
private TunnelPool _pool;
@@ -17,12 +20,42 @@ class RebuildJob extends JobImpl {
super(ctx);
_pool = pool;
_cfg = cfg;
- long rebuildOn = cfg.getExpiration() - pool.getSettings().getRebuildPeriod();
- rebuildOn -= ctx.random().nextInt(pool.getSettings().getRebuildPeriod()*2);
+ long rebuildOn;
+ if (_pool.getSettings().isExploratory()) {
+ rebuildOn = cfg.getExpiration() - (((pool.getSettings().getRebuildPeriod() * 7) / 2));
+ rebuildOn -= ctx.random().nextInt(pool.getSettings().getRebuildPeriod() / 2);
+ } else if ((pool.getSettings().getQuantity() + pool.getSettings().getBackupQuantity()) == 1) {
+ rebuildOn = cfg.getExpiration() - (pool.getSettings().getRebuildPeriod() * 4);
+ } else {
+ rebuildOn = cfg.getExpiration() - (pool.getSettings().getRebuildPeriod() * 2);
+ rebuildOn -= ctx.random().nextInt(pool.getSettings().getRebuildPeriod() * 2);
+ }
getTiming().setStartAfter(rebuildOn);
}
- public String getName() { return "Rebuild tunnel"; }
- public void runJob() {
- _pool.refreshBuilders();
+ public String getName() {
+ if (_pool.getSettings().isExploratory()) {
+ if (_pool.getSettings().isInbound()) {
+ return "Rebuild exploratory inbound tunnel";
+ } else {
+ return "Rebuild exploratory outbound tunnel";
+ }
+ } else {
+ StringBuffer rv = new StringBuffer(32);
+ if (_pool.getSettings().isInbound())
+ rv.append("Rebuild inbound client tunnel for ");
+ else
+ rv.append("Rebuild outbound client tunnel for ");
+ if (_pool.getSettings().getDestinationNickname() != null)
+ rv.append(_pool.getSettings().getDestinationNickname());
+ else
+ rv.append(_pool.getSettings().getDestination().toBase64().substring(0,4));
+ return rv.toString();
+ }
}
-}
\ No newline at end of file
+ public void runJob() {
+ if (_pool.getSettings().isExploratory())
+ _pool.refreshBuilders(4, 4);
+ else
+ _pool.refreshBuilders(1, 4);
+ }
+}
diff --git a/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java b/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java
index 208d8031f..44f075ebf 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/RequestTunnelJob.java
@@ -66,9 +66,15 @@ public class RequestTunnelJob extends JobImpl {
ctx.statManager().createRateStat("tunnel.receiveRejectionBandwidth", "How often we are rejected due to bandwidth overload?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("tunnel.receiveRejectionCritical", "How often we are rejected due to critical failure?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("tunnel.buildFailure", "What hop was being requested when a nonexploratory tunnel request failed?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
- ctx.statManager().createRateStat("tunnel.buildExploratoryFailure", "What hop was beiing requested when an exploratory tunnel request failed?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
+ ctx.statManager().createRateStat("tunnel.buildExploratoryFailure", "What hop was being requested when an exploratory tunnel request failed?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
+ ctx.statManager().createRateStat("tunnel.buildExploratoryFailure1Hop", "What hop was being requested when a 1 hop exploratory tunnel request failed?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
+ ctx.statManager().createRateStat("tunnel.buildExploratoryFailure2Hop", "What hop was being requested when a 2 hop exploratory tunnel request failed?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
+ ctx.statManager().createRateStat("tunnel.buildExploratoryFailure3Hop", "What hop was being requested when a 3 hop exploratory tunnel request failed?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("tunnel.buildSuccess", "How often we succeed building a non-exploratory tunnel?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("tunnel.buildExploratorySuccess", "How often we succeed building an exploratory tunnel?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
+ ctx.statManager().createRateStat("tunnel.buildExploratorySuccess1Hop", "How often we succeed building a 1 hop exploratory tunnel?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
+ ctx.statManager().createRateStat("tunnel.buildExploratorySuccess2Hop", "How often we succeed building a 2 hop exploratory tunnel?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
+ ctx.statManager().createRateStat("tunnel.buildExploratorySuccess3Hop", "How often we succeed building a 3 hop exploratory tunnel?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("tunnel.buildPartialTime", "How long a non-exploratory request took to be accepted?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
ctx.statManager().createRateStat("tunnel.buildExploratoryPartialTime", "How long an exploratory request took to be accepted?", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
@@ -263,9 +269,16 @@ public class RequestTunnelJob extends JobImpl {
_log.info("tunnel building failed: " + _config + " at hop " + _currentHop);
if (_onFailed != null)
getContext().jobQueue().addJob(_onFailed);
- if (_isExploratory)
- getContext().statManager().addRateData("tunnel.buildExploratoryFailure", _currentHop, _config.getLength());
- else
+ if (_isExploratory) {
+ int i = _config.getLength();
+ getContext().statManager().addRateData("tunnel.buildExploratoryFailure", _currentHop, i);
+ if (i == 2)
+ getContext().statManager().addRateData("tunnel.buildExploratoryFailure1Hop", _currentHop, i);
+ else if (i == 3)
+ getContext().statManager().addRateData("tunnel.buildExploratoryFailure2Hop", _currentHop, i);
+ else if (i == 4)
+ getContext().statManager().addRateData("tunnel.buildExploratoryFailure3Hop", _currentHop, i);
+ } else
getContext().statManager().addRateData("tunnel.buildFailure", _currentHop, _config.getLength());
}
@@ -284,9 +297,16 @@ public class RequestTunnelJob extends JobImpl {
} else {
if (_onCreated != null)
getContext().jobQueue().addJob(_onCreated);
- if (_isExploratory)
+ if (_isExploratory) {
+ int i = _config.getLength();
getContext().statManager().addRateData("tunnel.buildExploratorySuccess", 1, 0);
- else
+ if (i == 2)
+ getContext().statManager().addRateData("tunnel.buildExploratorySuccess1Hop", 1, 0);
+ else if (i == 3)
+ getContext().statManager().addRateData("tunnel.buildExploratorySuccess2Hop", 1, 0);
+ else if (i == 4)
+ getContext().statManager().addRateData("tunnel.buildExploratorySuccess3Hop", 1, 0);
+ } else
getContext().statManager().addRateData("tunnel.buildSuccess", 1, 0);
}
}
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelBuilder.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelBuilder.java
index 2655fa204..1d0a5adba 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelBuilder.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelBuilder.java
@@ -110,7 +110,7 @@ public class TunnelBuilder {
public void runJob() {
// yikes, nothing left, lets get some backup (if we're allowed)
_pool.getManager().buildComplete();
- _pool.refreshBuilders();
+ _pool.refreshBuilders(1, 4);
}
}
}
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java
index 980b1cc55..0a07dc3ea 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java
@@ -26,7 +26,10 @@ abstract class TunnelPeerSelector {
protected int getLength(RouterContext ctx, TunnelPoolSettings settings) {
int length = settings.getLength();
- if (settings.getLengthVariance() != 0) {
+ int override = settings.getLengthOverride();
+ if (override != 0)
+ length = override;
+ else if (settings.getLengthVariance() != 0) {
int skew = settings.getLengthVariance();
if (skew > 0)
length += ctx.random().nextInt(skew+1);
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
index 16f3d06ea..34ec54c26 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
@@ -37,7 +37,7 @@ public class TunnelPool {
private long _lastSelectionPeriod;
/**
- * Only 5 builds per minute per pool, even if we have failing tunnels,
+ * Only 10 builds per minute per pool, even if we have failing tunnels,
* etc. On overflow, the necessary additional tunnels are built by the
* RefreshJob
*/
@@ -65,7 +65,7 @@ public class TunnelPool {
_alive = true;
_refreshJob.getTiming().setStartAfter(_context.clock().now() + 60*1000);
_context.jobQueue().addJob(_refreshJob);
- int added = refreshBuilders();
+ int added = refreshBuilders(0, 0);
if (added <= 0) {
// we just reconnected and didn't require any new tunnel builders.
// however, we /do/ want a leaseSet, so build one
@@ -85,12 +85,17 @@ public class TunnelPool {
_lastSelected = null;
}
- private int countUsableTunnels() {
+ /**
+ * Return number of tunnels expiring greater than
+ * timeFactor * RebuildPeriod from now
+ *
+ */
+ private int countUsableTunnels(int timeFactor) {
int valid = 0;
synchronized (_tunnels) {
for (int i = 0; i < _tunnels.size(); i++) {
TunnelInfo info = (TunnelInfo)_tunnels.get(i);
- if (info.getExpiration() > _context.clock().now() + 3*_settings.getRebuildPeriod())
+ if (info.getExpiration() > _context.clock().now() + (timeFactor * _settings.getRebuildPeriod()))
valid++;
}
}
@@ -99,21 +104,31 @@ public class TunnelPool {
/**
* Fire up as many buildTunnel tasks as necessary, returning how many
- * were added
+ * were added.
+ * Build maxBuild tunnels (0 = unlimited), use timeFactor * RebuildPeriod.
+ * Fire off up to six extra jobs if an exploratory tunnel is
+ * requested by RebuildJob or tunnelFailed (maxBuild > 1).
+ * Throttle builds to a maximum per minute; reduce maximum if job lag is high,
+ * or if we have network errors which indicate we are disconnected from the network.
+ * Override pool length setting and build a 1-hop tunnel if time is short.
*
*/
- int refreshBuilders() {
+ int refreshBuilders(int maxBuild, int timeFactor) {
if ( (_settings.getDestination() != null) && (!_context.clientManager().isLocal(_settings.getDestination())) )
_alive = false;
if (!_alive) return 0;
// only start up new build tasks if we need more of 'em
- int target = _settings.getQuantity() + _settings.getBackupQuantity();
- int usableTunnels = countUsableTunnels();
+ int baseTarget = _settings.getQuantity() + _settings.getBackupQuantity();
+ int target = baseTarget;
+ int usableTunnels = countUsableTunnels(timeFactor);
+ if (_settings.isExploratory() && target > 0 && maxBuild > 1)
+ target+= 6;
- if ( (target > usableTunnels) && (_log.shouldLog(Log.INFO)) )
- _log.info(toString() + ": refreshing builders, previously had " + usableTunnels
+ if ( (target > usableTunnels) )
+ if ( (target > usableTunnels) && (_log.shouldLog(Log.INFO)) )
+ _log.info(toString() + ": refreshing builders, previously had " + usableTunnels
+ ", want a total of " + target + ", creating "
- + (target-usableTunnels) + " new ones.");
+ + (target-usableTunnels) + " new ones (" + maxBuild + " max).");
if (target > usableTunnels) {
long minute = _context.clock().now();
@@ -123,9 +138,34 @@ public class TunnelPool {
_buildsThisMinute = 0;
}
int build = (target - usableTunnels);
- if (build > (MAX_BUILDS_PER_MINUTE - _buildsThisMinute))
- build = (MAX_BUILDS_PER_MINUTE - _buildsThisMinute);
-
+ if (maxBuild > 0 && build > maxBuild)
+ build = maxBuild;
+ int buildThrottle = MAX_BUILDS_PER_MINUTE;
+ int lag = (int) _context.statManager().getRate("jobQueue.jobLag").getRate(60*1000).getAverageValue();
+ int netErrors = (int) _context.statManager().getRate("udp.sendException").getRate(60*1000).getLastEventCount();
+ if (lag > 3 * 1000 || netErrors > 5) {
+ if (_log.shouldLog(Log.WARN))
+ _log.warn("Throttling tunnel builds lag = " + lag + "; net errors = " + netErrors);
+ if (_settings.isExploratory())
+ buildThrottle = 3;
+ else
+ buildThrottle = 1;
+ } else if (lag > 1 * 1000) {
+ if (_settings.isExploratory())
+ buildThrottle = 5;
+ else
+ buildThrottle = 2;
+ }
+ if (build > (buildThrottle - _buildsThisMinute))
+ build = (buildThrottle - _buildsThisMinute);
+ if (build <= 0) return 0;
+
+ if ((_settings.isExploratory() && baseTarget > countUsableTunnels(1)) ||
+ ((!_settings.isExploratory()) && baseTarget > countUsableTunnels(0)))
+ _settings.setLengthOverride(1);
+ else
+ _settings.setLengthOverride(0);
+
int wanted = build;
build = _manager.allocateBuilds(build);
@@ -255,7 +295,7 @@ public class TunnelPool {
if (_settings != null) {
if (_log.shouldLog(Log.INFO))
_log.info(toString() + ": Settings updated on the pool: " + settings);
- refreshBuilders(); // to start/stop new sequences, in case the quantities changed
+ refreshBuilders(1, 4); // to start/stop new sequences, in case the quantities changed
}
}
public TunnelPeerSelector getSelector() { return _peerSelector; }
@@ -278,8 +318,6 @@ public class TunnelPool {
if (ls != null)
_context.clientManager().requestLeaseSet(_settings.getDestination(), ls);
-
- refreshBuilders();
}
public void removeTunnel(TunnelInfo info) {
@@ -319,7 +357,6 @@ public class TunnelPool {
_manager.removeTunnels(_settings.getDestination());
return;
}
- refreshBuilders();
}
public void tunnelFailed(PooledTunnelCreatorConfig cfg) {
@@ -343,15 +380,27 @@ public class TunnelPool {
if (_settings.isInbound() && (_settings.getDestination() != null) ) {
if (ls != null) {
_context.clientManager().requestLeaseSet(_settings.getDestination(), ls);
+ if (_settings.isExploratory())
+ refreshBuilders(3, 4);
+ else
+ refreshBuilders(1, 4);
} else {
if (_log.shouldLog(Log.WARN))
_log.warn(toString() + ": unable to build a new leaseSet on failure (" + remaining
+ " remaining), request a new tunnel");
if (remaining < _settings.getBackupQuantity() + _settings.getQuantity())
- buildFallback();
+ if (!buildFallback())
+ if (_settings.isExploratory())
+ refreshBuilders(3, 4);
+ else
+ refreshBuilders(1, 4);
}
+ } else {
+ if (_settings.isExploratory())
+ refreshBuilders(3, 4);
+ else
+ refreshBuilders(1, 4);
}
- refreshBuilders();
}
void refreshLeaseSet() {
@@ -377,18 +426,24 @@ public class TunnelPool {
}
}
- void buildFallback() {
+ /**
+ * Return true if a fallback tunnel is built
+ *
+ */
+ boolean buildFallback() {
int quantity = _settings.getBackupQuantity() + _settings.getQuantity();
- int usable = countUsableTunnels();
- if (usable >= quantity) return;
+ int usable = countUsableTunnels(1);
+ if (usable >= quantity) return false;
- if (_log.shouldLog(Log.INFO))
- _log.info(toString() + ": building a fallback tunnel (usable: " + usable + " needed: " + quantity + ")");
- if ( (usable == 0) && (_settings.getAllowZeroHop()) )
+ if ( (usable == 0) && (_settings.getAllowZeroHop()) ) {
+ if (_log.shouldLog(Log.INFO))
+ _log.info(toString() + ": building a fallback tunnel (usable: " + usable + " needed: " + quantity + ")");
_builder.buildTunnel(_context, this, true);
+ return true;
+ }
//else
// _builder.buildTunnel(_context, this);
- refreshBuilders();
+ return false;
}
/**
@@ -476,12 +531,16 @@ public class TunnelPool {
public RefreshJob(RouterContext ctx) {
super(ctx);
}
- public String getName() { return "Refresh pool"; }
+ public String getName() { return "Refresh " + TunnelPool.this.toString(); }
public void runJob() {
if (!_alive) return;
- int added = refreshBuilders();
+ int added;
+ if (_settings.isExploratory())
+ added = refreshBuilders(0, 2);
+ else
+ added = refreshBuilders(0, 1);
if ( (added > 0) && (_log.shouldLog(Log.WARN)) )
- _log.warn("Passive rebuilding a tunnel for " + TunnelPool.this.toString());
+ _log.warn("Additional parallel rebuilding of tunnel for " + TunnelPool.this.toString());
requeue(30*1000);
}
}
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
index 4367b437a..a2a9de019 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
@@ -154,6 +154,38 @@ public class TunnelPoolManager implements TunnelManagerFacade {
else
return _outboundExploratory.size();
}
+ public int getInboundClientTunnelCount() {
+ int count = 0;
+ List destinations = null;
+ synchronized (_clientInboundPools) {
+ destinations = new ArrayList(_clientInboundPools.keySet());
+ }
+ for (int i = 0; i < destinations.size(); i++) {
+ Hash client = (Hash)destinations.get(i);
+ TunnelPool pool = null;
+ synchronized (_clientInboundPools) {
+ pool = (TunnelPool)_clientInboundPools.get(client);
+ }
+ count += pool.listTunnels().size();
+ }
+ return count;
+ }
+ public int getOutboundClientTunnelCount() {
+ int count = 0;
+ List destinations = null;
+ synchronized (_clientInboundPools) {
+ destinations = new ArrayList(_clientOutboundPools.keySet());
+ }
+ for (int i = 0; i < destinations.size(); i++) {
+ Hash client = (Hash)destinations.get(i);
+ TunnelPool pool = null;
+ synchronized (_clientOutboundPools) {
+ pool = (TunnelPool)_clientOutboundPools.get(client);
+ }
+ count += pool.listTunnels().size();
+ }
+ return count;
+ }
public int getParticipatingCount() { return _context.tunnelDispatcher().getParticipatingCount(); }
public long getLastParticipatingExpiration() { return _context.tunnelDispatcher().getLastParticipatingExpiration(); }
@@ -240,6 +272,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
}
}
inbound.startup();
+ try { Thread.sleep(3*1000); } catch (InterruptedException ie) {}
outbound.startup();
}
@@ -316,6 +349,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
_inboundExploratory = new TunnelPool(_context, this, inboundSettings, selector, builder);
_inboundExploratory.startup();
+ try { Thread.sleep(3*1000); } catch (InterruptedException ie) {}
TunnelPoolSettings outboundSettings = new TunnelPoolSettings();
outboundSettings.setIsExploratory(true);
outboundSettings.setIsInbound(false);
@@ -406,10 +440,10 @@ public class TunnelPoolManager implements TunnelManagerFacade {
out.write("
n/a
");
long timeLeft = cfg.getExpiration()-_context.clock().now();
if (timeLeft > 0)
- out.write("
" + DataHelper.formatDuration(timeLeft) + "
");
+ out.write("
" + DataHelper.formatDuration(timeLeft) + "
");
else
- out.write("
(grace period)
");
- out.write("
" + cfg.getProcessedMessagesCount() + "KB
");
+ out.write("
(grace period)
");
+ out.write("
" + cfg.getProcessedMessagesCount() + "KB
");
out.write("\n");
processed += cfg.getProcessedMessagesCount();
}
@@ -441,8 +475,8 @@ public class TunnelPoolManager implements TunnelManagerFacade {
out.write("