Author: toad
Date: 2007-06-23 20:46:04 +0000 (Sat, 23 Jun 2007)
New Revision: 13740
Modified:
trunk/freenet/src/freenet/io/comm/DMT.java
trunk/freenet/src/freenet/node/RequestHandler.java
trunk/freenet/src/freenet/node/RequestSender.java
Log:
Include TIME_LEFT on FNPRecentlyFailed. This is vital to prevent looping, if
FNPRecentlyFailed's can cause FT entries - which they must, otherwise they will
not be very helpful in reducing traffic.
Modified: trunk/freenet/src/freenet/io/comm/DMT.java
===================================================================
--- trunk/freenet/src/freenet/io/comm/DMT.java 2007-06-23 19:23:12 UTC (rev
13739)
+++ trunk/freenet/src/freenet/io/comm/DMT.java 2007-06-23 20:46:04 UTC (rev
13740)
@@ -112,6 +112,7 @@
public static final String BWLIMIT_DELAY_TIME = "bwlimitDelayTime";
public static final String TIME = "time";
public static final String FORK_COUNT = "forkCount";
+ public static final String TIME_LEFT = "timeLeft";
//Diagnostic
public static final MessageType ping = new MessageType("ping") {{
@@ -523,11 +524,13 @@
public static final MessageType FNPRecentlyFailed = new
MessageType("FNPRecentlyFailed") {{
addField(UID, Long.class);
+ addField(TIME_LEFT, Integer.class);
}};
- public static final Message createFNPRecentlyFailed(long id) {
+ public static final Message createFNPRecentlyFailed(long id, int
timeLeft) {
Message msg = new Message(FNPRecentlyFailed);
msg.set(UID, id);
+ msg.set(TIME_LEFT, timeLeft);
return msg;
}
@@ -711,6 +714,16 @@
return msg;
}
+ public static MessageType FNPOfferKey = new MessageType("FNPOfferKey")
{{
+ addField(KEY, Key.class);
+ }};
+
+ public static Message createFNPOfferKey(Key key) {
+ Message msg = new Message(FNPOfferKey);
+ msg.set(KEY, key);
+ return msg;
+ }
+
public static final MessageType FNPPing = new MessageType("FNPPing") {{
addField(PING_SEQNO, Integer.class);
}};
Modified: trunk/freenet/src/freenet/node/RequestHandler.java
===================================================================
--- trunk/freenet/src/freenet/node/RequestHandler.java 2007-06-23 19:23:12 UTC
(rev 13739)
+++ trunk/freenet/src/freenet/node/RequestHandler.java 2007-06-23 20:46:04 UTC
(rev 13740)
@@ -146,7 +146,7 @@
source.sendSync(dnf, this);
return;
case RequestSender.RECENTLY_FAILED:
- Message rf = DMT.createFNPRecentlyFailed(uid);
+ Message rf = DMT.createFNPRecentlyFailed(uid,
rs.getRecentlyFailedTimeLeft());
source.sendSync(rf, this);
return;
case RequestSender.GENERATED_REJECTED_OVERLOAD:
Modified: trunk/freenet/src/freenet/node/RequestSender.java
===================================================================
--- trunk/freenet/src/freenet/node/RequestSender.java 2007-06-23 19:23:12 UTC
(rev 13739)
+++ trunk/freenet/src/freenet/node/RequestSender.java 2007-06-23 20:46:04 UTC
(rev 13740)
@@ -154,6 +154,11 @@
Message req = createDataRequest();
+ // Not possible to get an accurate time for sending, guaranteed to
be not later than the time of receipt.
+ // Why? Because by the time the sent() callback gets called, it
may already have been acked, under heavy load.
+ // So take it from when we first started to try to send the
request.
+ // See comments below when handling FNPRecentlyFailed for why we
need this.
+ long timeSentRequest = System.currentTimeMillis();
next.sendSync(req, this);
@@ -277,7 +282,65 @@
if(msg.getSpec() == DMT.FNPRecentlyFailed) {
next.successNotOverload();
- finish(RECENTLY_FAILED, next);
+ /*
+ * Must set a correct recentlyFailedTimeLeft before
calling this finish(), because it will be
+ * passed to the handler.
+ *
+ * It is *VITAL* that the TIME_LEFT we pass on is not
larger than it should be.
+ * It is somewhat less important that it is not too
much smaller than it should be.
+ *
+ * Why? Because:
+ * 1) We have to use FNPRecentlyFailed to create
failure table entries. Because otherwise,
+ * the failure table is of little value: A request is
routed through a node, which gets a DNF,
+ * and adds a failure table entry. Other requests then
go through that node via other paths.
+ * They are rejected with FNPRecentlyFailed - not with
DataNotFound. If this does not create
+ * failure table entries, more requests will be
pointlessly routed through that chain.
+ *
+ * 2) If we use a fixed timeout on receiving
FNPRecentlyFailed, they can be self-seeding.
+ * What this means is A sends a request to B, which
DNFs. This creates a failure table entry
+ * which lasts for 10 minutes. 5 minutes later, A sends
another request to B, which is killed
+ * with FNPRecentlyFailed because of the failure table
entry. B's failure table lasts for
+ * another 5 minutes, but A's lasts for the full 10
minutes i.e. until 5 minutes after B's.
+ * After B's failure table entry has expired, but
before A's expires, B sends a request to A.
+ * A replies with FNPRecentlyFailed. Repeat ad
infinitum: A reinforces B's blocks, and B
+ * reinforces A's blocks!
+ *
+ * 3) This can still happen even if we check where the
request is coming from. A loop could
+ * very easily form: A - B - C - A. A requests from B,
DNFs (assume the request comes in from
+ * outside, there are more nodes. C requests from A,
sets up a block. B's block expires, C's
+ * is still active. A requests from B which requests
from C ... and it goes round again.
+ *
+ * 4) It is exactly the same if we specify a timeout,
unless the timeout can be guaranteed to
+ * not increase the expiry time.
+ */
+
+ // First take the original TIME_LEFT. This will start
at 10 minutes if we get rejected in
+ // the same millisecond as the failure table block was
added.
+ int timeLeft = msg.getInt(DMT.TIME_LEFT);
+ int origTimeLeft = timeLeft;
+
+ if(timeLeft <= 0) {
+ Logger.error(this, "Impossible:
timeLeft="+timeLeft);
+ origTimeLeft = 0;
+ timeLeft=1000; // arbitrary default...
+ }
+
+ // This is in theory relative to when the request was
received by the node. Lets make it relative
+ // to a known event before that: the time when we sent
the request.
+
+ long timeSinceSent = Math.max(0,
(System.currentTimeMillis() - timeSentRequest));
+ timeLeft -= timeSinceSent;
+
+ // Subtract 1% for good measure / to compensate for
dodgy clocks
+ timeLeft -= origTimeLeft / 100;
+
+ if(timeLeft <= 0) {
+ // No timeout left, cool
+ finish(DATA_NOT_FOUND, next);
+ } else {
+ // Some timeout left
+ finish(RECENTLY_FAILED, next);
+ }
return;
}
@@ -609,4 +672,10 @@
public void sentPayload(int x) {
node.sentPayload(x);
}
+
+ private int recentlyFailedTimeLeft;
+
+ synchronized int getRecentlyFailedTimeLeft() {
+ return recentlyFailedTimeLeft;
+ }
}